This seems to be the easiest thing to do, but it's just not working. In a normal browser the .html and .js files works perfectly, but in the Chrome/Firefox extension the onClick function is not performing what it's supposed to do.
.js file:
function hellYeah(text) {
document.getElementById("text-holder").innerHTML = text;
}
.html file:
<!doctype html>
<html>
<head>
<title>
Getting Started Extension's Popup
</title>
<script src="popup.js"></script>
</head>
<body>
<div id="text-holder">
ha
</div>
<br />
<a onClick=hellYeah("xxx")>
hyhy
</a>
</body>
</html>
So basically once the user clicks "hyhy", "ha" should change into "xxx". And again - it works perfectly in the browser but does not work in the extension. Do you know why? Just in case I'm attaching the manifest.json below as well.
manifest.json:
{
"name": "My First Extension",
"version": "1.0",
"manifest_version": 2,
"description": "The first extension that I made.",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://api.flickr.com/"
]
}
Chrome Extensions don't allow you to have inline JavaScript (documentation).
The same goes for Firefox WebExtensions (documentation).
You are going to have to do something similar to this:
Assign an ID to the link (<a onClick=hellYeah("xxx")> becomes <a id="link">), and use addEventListener to bind the event. Put the following in your popup.js file:
document.addEventListener('DOMContentLoaded', function() {
var link = document.getElementById('link');
// onClick's logic below:
link.addEventListener('click', function() {
hellYeah('xxx');
});
});
popup.js should be loaded as a separate script file:
<script src="popup.js"></script>
Reason
This does not work, because Chrome forbids any kind of inline code in extensions via Content Security Policy.
Inline JavaScript will not be executed. This restriction bans both inline <script> blocks and inline event handlers (e.g. <button onclick="...">).
How to detect
If this is indeed the problem, Chrome would produce the following error in the console:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
To access a popup's JavaScript console (which is useful for debug in general), right-click your extension's button and select "Inspect popup" from the context menu.
More information on debugging a popup is available here.
How to fix
One needs to remove all inline JavaScript. There is a guide in Chrome documentation.
Suppose the original looks like:
<a onclick="handler()">Click this</a> <!-- Bad -->
One needs to remove the onclick attribute and give the element a unique id:
<a id="click-this">Click this</a> <!-- Fixed -->
And then attach the listener from a script (which must be in a .js file, suppose popup.js):
// Pure JS:
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("click-this").addEventListener("click", handler);
});
// The handler also must go in a .js file
function handler() {
/* ... */
}
Note the wrapping in a DOMContentLoaded event. This ensures that the element exists at the time of execution. Now add the script tag, for instance in the <head> of the document:
<script src="popup.js"></script>
Alternative if you're using jQuery:
// jQuery
$(document).ready(function() {
$("#click-this").click(handler);
});
Relaxing the policy
Q: The error mentions ways to allow inline code. I don't want to / can't change my code, how do I enable inline scripts?
A: Despite what the error says, you cannot enable inline script:
There is no mechanism for relaxing the restriction against executing inline JavaScript. In particular, setting a script policy that includes 'unsafe-inline' will have no effect.
Update: Since Chrome 46, it's possible to whitelist specific inline code blocks:
As of Chrome 46, inline scripts can be whitelisted by specifying the base64-encoded hash of the source code in the policy. This hash must be prefixed by the used hash algorithm (sha256, sha384 or sha512). See Hash usage for <script> elements for an example.
However, I do not readily see a reason to use this, and it will not enable inline attributes like onclick="code".
I had the same problem, and didn´t want to rewrite the code, so I wrote a function to modify the code and create the inline declarated events:
function compile(qSel){
var matches = [];
var match = null;
var c = 0;
var html = $(qSel).html();
var pattern = /(<(.*?)on([a-zA-Z]+)\s*=\s*('|")(.*)('|")(.*?))(>)/mg;
while (match = pattern.exec(html)) {
var arr = [];
for (i in match) {
if (!isNaN(i)) {
arr.push(match[i]);
}
}
matches.push(arr);
}
var items_with_events = [];
var compiledHtml = html;
for ( var i in matches ){
var item_with_event = {
custom_id : "my_app_identifier_"+i,
code : matches[i][5],
on : matches[i][3],
};
items_with_events.push(item_with_event);
compiledHtml = compiledHtml.replace(/(<(.*?)on([a-zA-Z]+)\s*=\s*('|")(.*)('|")(.*?))(>)/m, "<$2 custom_id='"+item_with_event.custom_id+"' $7 $8");
}
$(qSel).html(compiledHtml);
for ( var i in items_with_events ){
$("[custom_id='"+items_with_events[i].custom_id+"']").bind(items_with_events[i].on, function(){
eval(items_with_events[i].code);
});
}
}
$(document).ready(function(){
compile('#content');
})
This should remove all inline events from the selected node, and recreate them with jquery instead.
I decide to publish my example that I used in my case. I tried to replace content in div using a script. My problem was that Chrome did not recognized / did not run that script.
In more detail What I wanted to do: To click on a link, and that link to "read" an external html file, that it will be loaded in a div section.
I found out that by placing the script before the DIV with ID that
was called, the script did not work.
If the script was in another DIV, also it does not work
The script must be coded using document.addEventListener('DOMContentLoaded', function() as it was told
<body>
<a id=id_page href ="#loving" onclick="load_services()"> loving </a>
<script>
// This script MUST BE under the "ID" that is calling
// Do not transfer it to a differ DIV than the caller "ID"
document.getElementById("id_page").addEventListener("click", function(){
document.getElementById("mainbody").innerHTML = '<object data="Services.html" class="loving_css_edit"; ></object>'; });
</script>
</body>
<div id="mainbody" class="main_body">
"here is loaded the external html file when the loving link will
be clicked. "
</div>
As already mentioned, Chrome Extensions don't allow to have inline JavaScript due to security reasons so you can try this workaround as well.
HTML file
<!doctype html>
<html>
<head>
<title>
Getting Started Extension's Popup
</title>
<script src="popup.js"></script>
</head>
<body>
<div id="text-holder">ha</div><br />
<a class="clickableBtn">
hyhy
</a>
</body>
</html>
<!doctype html>
popup.js
window.onclick = function(event) {
var target = event.target ;
if(target.matches('.clickableBtn')) {
var clickedEle = document.activeElement.id ;
var ele = document.getElementById(clickedEle);
alert(ele.text);
}
}
Or if you are having a Jquery file included then
window.onclick = function(event) {
var target = event.target ;
if(target.matches('.clickableBtn')) {
alert($(target).text());
}
}
I have 2 HTML files, suppose a.html and b.html. In a.html I want to include b.html.
In JSF I can do it like that:
<ui:include src="b.xhtml" />
It means that inside a.xhtml file, I can include b.xhtml.
How can we do it in *.html file?
In my opinion the best solution uses jQuery:
a.html:
<html>
<head>
<script src="jquery.js"></script>
<script>
$(function(){
$("#includedContent").load("b.html");
});
</script>
</head>
<body>
<div id="includedContent"></div>
</body>
</html>
b.html:
<p>This is my include file</p>
This method is a simple and clean solution to my problem.
The jQuery .load() documentation is here.
Expanding lolo's answer, here is a little more automation if you have to include a lot of files. Use this JS code:
$(function () {
var includes = $('[data-include]')
$.each(includes, function () {
var file = 'views/' + $(this).data('include') + '.html'
$(this).load(file)
})
})
And then to include something in the html:
<div data-include="header"></div>
<div data-include="footer"></div>
Which would include the file views/header.html and views/footer.html.
My solution is similar to the one of lolo above. However, I insert the HTML code via JavaScript's document.write instead of using jQuery:
a.html:
<html>
<body>
<h1>Put your HTML content before insertion of b.js.</h1>
...
<script src="b.js"></script>
...
<p>And whatever content you want afterwards.</p>
</body>
</html>
b.js:
document.write('\
\
<h1>Add your HTML code here</h1>\
\
<p>Notice however, that you have to escape LF's with a '\', just like\
demonstrated in this code listing.\
</p>\
\
');
The reason for me against using jQuery is that jQuery.js is ~90kb in size, and I want to keep the amount of data to load as small as possible.
In order to get the properly escaped JavaScript file without much work, you can use the following sed command:
sed 's/\\/\\\\/g;s/^.*$/&\\/g;s/'\''/\\'\''/g' b.html > escapedB.html
Or just use the following handy bash script published as a Gist on Github, that automates all necessary work, converting b.html to b.js:
https://gist.github.com/Tafkadasoh/334881e18cbb7fc2a5c033bfa03f6ee6
Credits to Greg Minshall for the improved sed command that also escapes back slashes and single quotes, which my original sed command did not consider.
Alternatively for browsers that support template literals the following also works:
b.js:
document.write(`
<h1>Add your HTML code here</h1>
<p>Notice, you do not have to escape LF's with a '\',
like demonstrated in the above code listing.
</p>
`);
Checkout HTML5 imports via Html5rocks tutorial
and at polymer-project
For example:
<head>
<link rel="import" href="/path/to/imports/stuff.html">
</head>
Shameless plug of a library that I wrote the solve this.
https://github.com/LexmarkWeb/csi.js
<div data-include="/path/to/include.html"></div>
The above will take the contents of /path/to/include.html and replace the div with it.
No need for scripts. No need to do any fancy stuff server-side (tho that would probably be a better option)
<iframe src="/path/to/file.html" seamless></iframe>
Since old browsers don't support seamless, you should add some css to fix it:
iframe[seamless] {
border: none;
}
Keep in mind that for browsers that don't support seamless, if you click a link in the iframe it will make the frame go to that url, not the whole window. A way to get around that is to have all links have target="_parent", tho the browser support is "good enough".
A simple server side include directive to include another file found in the same folder looks like this:
<!--#include virtual="a.html" -->
Also you can try:
<!--#include file="a.html" -->
A very old solution I did met my needs back then, but here's how to do it standards-compliant code:
<!--[if IE]>
<object classid="clsid:25336920-03F9-11CF-8FD0-00AA00686F13" data="some.html">
<p>backup content</p>
</object>
<![endif]-->
<!--[if !IE]> <-->
<object type="text/html" data="some.html">
<p>backup content</p>
</object>
<!--> <![endif]-->
Following works if html content from some file needs to be included:
For instance, the following line will include the contents of piece_to_include.html at the location where the OBJECT definition occurs.
...text before...
<OBJECT data="file_to_include.html">
Warning: file_to_include.html could not be included.
</OBJECT>
...text after...
Reference: http://www.w3.org/TR/WD-html40-970708/struct/includes.html#h-7.7.4
Here is my inline solution:
(() => {
const includes = document.getElementsByTagName('include');
[].forEach.call(includes, i => {
let filePath = i.getAttribute('src');
fetch(filePath).then(file => {
file.text().then(content => {
i.insertAdjacentHTML('afterend', content);
i.remove();
});
});
});
})();
<p>FOO</p>
<include src="a.html">Loading...</include>
<p>BAR</p>
<include src="b.html">Loading...</include>
<p>TEE</p>
In w3.js include works like this:
<body>
<div w3-include-HTML="h1.html"></div>
<div w3-include-HTML="content.html"></div>
<script>w3.includeHTML();</script>
</body>
For proper description look into this: https://www.w3schools.com/howto/howto_html_include.asp
As an alternative, if you have access to the .htaccess file on your server, you can add a simple directive that will allow php to be interpreted on files ending in .html extension.
RemoveHandler .html
AddType application/x-httpd-php .php .html
Now you can use a simple php script to include other files such as:
<?php include('b.html'); ?>
This is what helped me. For adding a block of html code from b.html to a.html, this should go into the head tag of a.html:
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
Then in the body tag, a container is made with an unique id and a javascript block to load the b.html into the container, as follows:
<div id="b-placeholder">
</div>
<script>
$(function(){
$("#b-placeholder").load("b.html");
});
</script>
I know this is a very old post, so some methods were not available back then.
But here is my very simple take on it (based on Lolo's answer).
It relies on the HTML5 data-* attributes and therefore is very generic in that is uses jQuery's for-each function to get every .class matching "load-html" and uses its respective 'data-source' attribute to load the content:
<div class="container-fluid">
<div class="load-html" id="NavigationMenu" data-source="header.html"></div>
<div class="load-html" id="MainBody" data-source="body.html"></div>
<div class="load-html" id="Footer" data-source="footer.html"></div>
</div>
<script src="js/jquery.min.js"></script>
<script>
$(function () {
$(".load-html").each(function () {
$(this).load(this.dataset.source);
});
});
</script>
Most of the solutions works but they have issue with jquery:
The issue is following code $(document).ready(function () { alert($("#includedContent").text()); } alerts nothing instead of alerting included content.
I write the below code, in my solution you can access to included content in $(document).ready function:
(The key is loading included content synchronously).
index.htm:
<html>
<head>
<script src="jquery.js"></script>
<script>
(function ($) {
$.include = function (url) {
$.ajax({
url: url,
async: false,
success: function (result) {
document.write(result);
}
});
};
}(jQuery));
</script>
<script>
$(document).ready(function () {
alert($("#test").text());
});
</script>
</head>
<body>
<script>$.include("include.inc");</script>
</body>
</html>
include.inc:
<div id="test">
There is no issue between this solution and jquery.
</div>
jquery include plugin on github
You can use a polyfill of HTML Imports (https://www.html5rocks.com/en/tutorials/webcomponents/imports/), or that simplified solution
https://github.com/dsheiko/html-import
For example, on the page you import HTML block like that:
<link rel="html-import" href="./some-path/block.html" >
The block may have imports of its own:
<link rel="html-import" href="./some-other-path/other-block.html" >
The importer replaces the directive with the loaded HTML pretty much like SSI
These directives will be served automatically as soon as you load this small JavaScript:
<script async src="./src/html-import.js"></script>
It will process the imports when DOM is ready automatically. Besides, it exposes an API that you can use to run manually, to get logs and so on. Enjoy :)
Here's my approach using Fetch API and async function
<div class="js-component" data-name="header" data-ext="html"></div>
<div class="js-component" data-name="footer" data-ext="html"></div>
<script>
const components = document.querySelectorAll('.js-component')
const loadComponent = async c => {
const { name, ext } = c.dataset
const response = await fetch(`${name}.${ext}`)
const html = await response.text()
c.innerHTML = html
}
[...components].forEach(loadComponent)
</script>
To insert contents of the named file:
<!--#include virtual="filename.htm"-->
Another approach using Fetch API with Promise
<html>
<body>
<div class="root" data-content="partial.html">
<script>
const root = document.querySelector('.root')
const link = root.dataset.content;
fetch(link)
.then(function (response) {
return response.text();
})
.then(function (html) {
root.innerHTML = html;
});
</script>
</body>
</html>
Did you try a iFrame injection?
It injects the iFrame in the document and deletes itself (it is supposed to be then in the HTML DOM)
<iframe src="header.html" onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"></iframe>
Regards
The Athari´s answer (the first!) was too much conclusive! Very Good!
But if you would like to pass the name of the page to be included as URL parameter, this post has a very nice solution to be used combined with:
http://www.jquerybyexample.net/2012/06/get-url-parameters-using-jquery.html
So it becomes something like this:
Your URL:
www.yoursite.com/a.html?p=b.html
The a.html code now becomes:
<html>
<head>
<script src="jquery.js"></script>
<script>
function GetURLParameter(sParam)
{
var sPageURL = window.location.search.substring(1);
var sURLVariables = sPageURL.split('&');
for (var i = 0; i < sURLVariables.length; i++)
{
var sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] == sParam)
{
return sParameterName[1];
}
}
}
$(function(){
var pinc = GetURLParameter('p');
$("#includedContent").load(pinc);
});
</script>
</head>
<body>
<div id="includedContent"></div>
</body>
</html>
It worked very well for me!
I hope have helped :)
html5rocks.com has a very good tutorial on this stuff, and this might be a little late, but I myself didn't know this existed. w3schools also has a way to do this using their new library called w3.js. The thing is, this requires the use of a web server and and HTTPRequest object. You can't actually load these locally and test them on your machine. What you can do though, is use polyfills provided on the html5rocks link at the top, or follow their tutorial. With a little JS magic, you can do something like this:
var link = document.createElement('link');
if('import' in link){
//Run import code
link.setAttribute('rel','import');
link.setAttribute('href',importPath);
document.getElementsByTagName('head')[0].appendChild(link);
//Create a phantom element to append the import document text to
link = document.querySelector('link[rel="import"]');
var docText = document.createElement('div');
docText.innerHTML = link.import;
element.appendChild(docText.cloneNode(true));
} else {
//Imports aren't supported, so call polyfill
importPolyfill(importPath);
}
This will make the link (Can change to be the wanted link element if already set), set the import (unless you already have it), and then append it. It will then from there take that and parse the file in HTML, and then append it to the desired element under a div. This can all be changed to fit your needs from the appending element to the link you are using. I hope this helped, it may irrelevant now if newer, faster ways have come out without using libraries and frameworks such as jQuery or W3.js.
UPDATE: This will throw an error saying that the local import has been blocked by CORS policy. Might need access to the deep web to be able to use this because of the properties of the deep web. (Meaning no practical use)
Use includeHTML (smallest js-lib: ~150 lines)
Loading HTML parts via HTML tag (pure js)
Supported load: async/sync, any deep recursive includes
Supported protocols: http://, https://, file:///
Supported browsers: IE 9+, FF, Chrome (and may be other)
USAGE:
1.Insert includeHTML into head section (or before body close tag) in HTML file:
<script src="js/includeHTML.js"></script>
2.Anywhere use includeHTML as HTML tag:
<div data-src="header.html"></div>
There is no direct HTML solution for the task for now. Even HTML Imports (which is permanently in draft) will not do the thing, because Import != Include and some JS magic will be required anyway.
I recently wrote a VanillaJS script that is just for inclusion HTML into HTML, without any complications.
Just place in your a.html
<link data-wi-src="b.html" />
<!-- ... and somewhere below is ref to the script ... -->
<script src="wm-html-include.js"> </script>
It is open-source and may give an idea (I hope)
You can do that with JavaScript's library jQuery like this:
HTML:
<div class="banner" title="banner.html"></div>
JS:
$(".banner").each(function(){
var inc=$(this);
$.get(inc.attr("title"), function(data){
inc.replaceWith(data);
});
});
Please note that banner.html should be located under the same domain your other pages are in otherwise your webpages will refuse the banner.html file due to Cross-Origin Resource Sharing policies.
Also, please note that if you load your content with JavaScript, Google will not be able to index it so it's not exactly a good method for SEO reasons.
Web Components
I create following web-component similar to JSF
<ui-include src="b.xhtml"><ui-include>
You can use it as regular html tag inside your pages (after including snippet js code)
customElements.define('ui-include', class extends HTMLElement {
async connectedCallback() {
let src = this.getAttribute('src');
this.innerHTML = await (await fetch(src)).text();;
}
})
ui-include { margin: 20px } /* example CSS */
<ui-include src="https://cors-anywhere.herokuapp.com/https://example.com/index.html"></ui-include>
<div>My page data... - in this snippet styles overlaps...</div>
<ui-include src="https://cors-anywhere.herokuapp.com/https://www.w3.org/index.html"></ui-include>
None of these solutions suit my needs. I was looking for something more PHP-like. This solution is quite easy and efficient, in my opinion.
include.js ->
void function(script) {
const { searchParams } = new URL(script.src);
fetch(searchParams.get('src')).then(r => r.text()).then(content => {
script.outerHTML = content;
});
}(document.currentScript);
index.html ->
<script src="/include.js?src=/header.html">
<main>
Hello World!
</main>
<script src="/include.js?src=/footer.html">
Simple tweaks can be made to create include_once, require, and require_once, which may all be useful depending on what you're doing. Here's a brief example of what that might look like.
include_once ->
var includedCache = includedCache || new Set();
void function(script) {
const { searchParams } = new URL(script.src);
const filePath = searchParams.get('src');
if (!includedCache.has(filePath)) {
fetch(filePath).then(r => r.text()).then(content => {
includedCache.add(filePath);
script.outerHTML = content;
});
}
}(document.currentScript);
Hope it helps!
Here is a great article, You can implement common library and just use below code to import any HTML files in one line.
<head>
<link rel="import" href="warnings.html">
</head>
You can also try Google Polymer
To get Solution working you need to include the file csi.min.js, which you can locate here.
As per the example shown on GitHub, to use this library you must include the file csi.js in your page header, then you need to add the data-include attribute with its value set to the file you want to include, on the container element.
Hide Copy Code
<html>
<head>
<script src="csi.js"></script>
</head>
<body>
<div data-include="Test.html"></div>
</body>
</html>
... hope it helps.
There are several types of answers here, but I never found the oldest tool in the use here:
"And all the other answers didn't work for me."
<html>
<head>
<title>pagetitle</title>
</head>
<frameset rows="*" framespacing="0" border="0" frameborder="no" frameborder="0">
<frame name="includeName" src="yourfileinclude.html" marginwidth="0" marginheight="0" scrolling="no" frameborder="0">
</frameset>
</html>
I am linking to the jQuery Mobile stylesheet on a CDN and would like to fall back to my local version of the stylesheet if the CDN fails. For scripts the solution is well known:
<!-- Load jQuery and jQuery mobile with fall back to local server -->
<script src="http://code.jquery.com/jquery-1.6.3.min.js"></script>
<script type="text/javascript">
if (typeof jQuery == 'undefined') {
document.write(unescape("%3Cscript src='jquery-1.6.3.min.js'%3E"));
}
</script>
I would like to do something similar for a style sheet:
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css" />
I am not sure if a similar approach can be achieved because I am not sure whether the browser blocks in the same way when linking a script as it does when loading a script (maybe it is possible to load a stylesheet in a script tag and then inject it into the page) ?
So my question is: How do I ensure a stylesheet is loaded locally if a CDN fails ?
One could use onerror for that:
<link rel="stylesheet" href="cdn.css" onerror="this.onerror=null;this.href='local.css';" />
The this.onerror=null; is to avoid endless loops in case the fallback it self is not available. But it could also be used to have multiple fallbacks.
However, this currently only works in Firefox and Chrome.
Update: Meanwhile, this seems to be supported by all common browsers.
Not cross-browser tested but I think this will work. Will have to be after you load jquery though, or you'll have to rewrite it in plain Javascript.
<script type="text/javascript">
$.each(document.styleSheets, function(i,sheet){
if(sheet.href=='http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css') {
var rules = sheet.rules ? sheet.rules : sheet.cssRules;
if (rules.length == 0) {
$('<link rel="stylesheet" type="text/css" href="path/to/local/jquery.mobile-1.0b3.min.css" />').appendTo('head');
}
}
})
</script>
Assuming you are using the same CDN for css and jQuery, why not just do one test and catch it all??
<link href="//ajax.googleapis.com/ajax/libs/jqueryui/1/themes/start/jquery-ui.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script type="text/javascript">
if (typeof jQuery == 'undefined') {
document.write(unescape('%3Clink rel="stylesheet" type="text/css" href="../../Content/jquery-ui-1.8.16.custom.css" /%3E'));
document.write(unescape('%3Cscript type="text/javascript" src="/jQuery/jquery-1.6.4.min.js" %3E%3C/script%3E'));
document.write(unescape('%3Cscript type="text/javascript" src="/jQuery/jquery-ui-1.8.16.custom.min.js" %3E%3C/script%3E'));
}
</script>
I guess the question is to detect whether a stylesheet is loaded or not. One possible approach is as follows:
1) Add a special rule to the end of your CSS file, like:
#foo { display: none !important; }
2) Add the corresponding div in your HTML:
<div id="foo"></div>
3) On document ready, check whether #foo is visible or not. If the stylesheet was loaded, it will not be visible.
Demo here -- loads jquery-ui smoothness theme; no rule is added to stylesheet.
this article suggests some solutions for the bootstrap css
http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/
alternatively this works for fontawesome
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<script>
(function($){
var $span = $('<span class="fa" style="display:none"></span>').appendTo('body');
if ($span.css('fontFamily') !== 'FontAwesome' ) {
// Fallback Link
$('head').append('<link href="/css/font-awesome.min.css" rel="stylesheet">');
}
$span.remove();
})(jQuery);
</script>
You might be able to test for the existence of the stylesheet in document.styleSheets.
var rules = [];
if (document.styleSheets[1].cssRules)
rules = document.styleSheets[i].cssRules
else if (document.styleSheets[i].rules)
rule= document.styleSheets[i].rules
Test for something specific to the CSS file you're using.
Here's an extension to katy lavallee's answer. I've wrapped everything in self-executing function syntax to prevent variable collisions. I've also made the script non-specific to a single link. I.E., now any stylesheet link with a "data-fallback" url attribute will automatically be parsed. You don't have to hard-code the urls into this script like before. Note that this should be run at the end of the <head> element rather than at the end of the <body> element, otherwise it could cause FOUC.
http://jsfiddle.net/skibulk/jnfgyrLt/
<link rel="stylesheet" type="text/css" href="broken-link.css" data-fallback="broken-link2.css">
.
(function($){
var links = {};
$( "link[data-fallback]" ).each( function( index, link ) {
links[link.href] = link;
});
$.each( document.styleSheets, function(index, sheet) {
if(links[sheet.href]) {
var rules = sheet.rules ? sheet.rules : sheet.cssRules;
if (rules.length == 0) {
link = $(links[sheet.href]);
link.attr( 'href', link.attr("data-fallback") );
}
}
});
})(jQuery);
Do you really want to go down this javascript route to load CSS in case a CDN fails?
I haven't thought all the performance implications through but you're going to lose control of when the CSS is loaded and in general for page load performance, CSS is the first thing you want to download after the HTML.
Why not handle this at the infrastructure level - map your own domain name to the CDN, give it a short TTL, monitor the files on the CDN (e.g. using Watchmouse or something else), if CDN fails, change the DNS to backup site.
Other options that might help are "cache forever" on static content but there's no guarantee the browser will keep them of course or using the app-cache.
In reality as someone said at the top, if your CDN is unreliable get a new one
Andy
Look at these functions:
$.ajax({
url:'CSS URL HERE',
type:'HEAD',
error: function()
{
AddLocalCss();
},
success: function()
{
//file exists
}
});
And here is vanilla JavaScript version:
function UrlExists(url)
{
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status!=404;
}
if (!UrlExists('CSS URL HERE') {
AddLocalCss();
}
Now the actual function:
function AddLocalCss(){
document.write('<link rel="stylesheet" type="text/css" href=" LOCAL CSS URL HERE">')
}
Just make sure AddLocalCss is called in the head.
You might also consider using one of the following ways explained in this answer:
Load using AJAX
$.get(myStylesLocation, function(css)
{
$('<style type="text/css"></style>')
.html(css)
.appendTo("head");
});
Load using dynamically-created
$('<link rel="stylesheet" type="text/css" href="'+myStylesLocation+'" >')
.appendTo("head");
Load using dynamically-created <style>
$('<style type="text/css"></style>')
.html('#import url("' + myStylesLocation + '")')
.appendTo("head");
or
$('<style type="text/css">#import url("' + myStylesLocation + '")</style>')
.appendTo("head");
I'd probably use something like yepnope.js
yepnope([{
load: 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js',
complete: function () {
if (!window.jQuery) {
yepnope('local/jquery.min.js');
}
}
}]);
Taken from the readme.
//(load your cdn lib here first)
<script>window.jQuery || document.write("<script src='//me.com/path/jquery-1.x.min.js'>\x3C/script>")</script>