web-dev-qa-db-fra.com

Comment revenir à la feuille de style locale (pas au script) si CDN échoue

Je crée un lien vers la feuille de style jQuery Mobile sur un CDN et je souhaite revenir à ma version locale de la feuille de style si le CDN échoue. Pour les scripts, la solution est bien connue:

<!-- 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>

Je voudrais faire quelque chose de similaire pour une feuille de style:

<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css" />

Je ne sais pas si une approche similaire peut être réalisée car je ne sais pas si le navigateur se bloque de la même manière lors de la liaison d'un script comme lors du chargement d'un script (il est peut-être possible de charger une feuille de style dans une balise de script, puis l'injecter dans la page)?

Ma question est donc la suivante: comment m'assurer qu'une feuille de style est chargée localement en cas d'échec d'un CDN?

102
ssn

Non testé sur plusieurs navigateurs, mais je pense que cela fonctionnera. Cela devra être après avoir chargé jquery, ou vous devrez le réécrire en Javascript simple.

<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>
60
katy lavallee

En supposant que vous utilisez le même CDN pour css et jQuery, pourquoi ne pas faire un seul test et tout attraper ??

<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>
28
Mike Wills

Je suppose que la question est de détecter si une feuille de style est chargée ou non. Une approche possible est la suivante:

1) Ajoutez une règle spéciale à la fin de votre fichier CSS, comme:

#foo { display: none !important; }

2) Ajoutez le div correspondant dans votre HTML:

<div id="foo"></div>

3) Lorsque le document est prêt, vérifiez si #foo est visible ou non. Si la feuille de style a été chargée, elle ne sera pas visible.

Démo ici - charge le thème de douceur jquery-ui; aucune règle n'est ajoutée à la feuille de style.

27
Salman A

cet article suggère quelques solutions pour le bootstrap css http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

alternativement, cela fonctionne pour 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>
7
dc2009

Voici une extension de la réponse de Katy Lavallee. J'ai tout enveloppé dans une syntaxe de fonction auto-exécutable pour éviter les collisions de variables. J'ai également rendu le script non spécifique à un seul lien. Par exemple, tout lien de feuille de style avec un attribut d'URL "data-fallback" sera automatiquement analysé. Vous n'avez pas à coder en dur les URL dans ce script comme auparavant. Notez que cela doit être exécuté à la fin du <head> élément plutôt qu'à la fin du <body> élément, sinon cela pourrait provoquer 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);
3
skibulk

Vous pourriez pouvoir tester l'existence de la feuille de style dans 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

Testez quelque chose de spécifique au fichier CSS que vous utilisez.

3
Stefan Kendall

Voulez-vous vraiment descendre cette route javascript pour charger CSS en cas d'échec d'un CDN?

Je n'ai pas réfléchi à toutes les implications en termes de performances, mais vous allez perdre le contrôle du moment où le CSS est chargé et, en général, pour les performances de chargement des pages, le CSS est la première chose que vous souhaitez télécharger après le HTML.

Pourquoi ne pas gérer cela au niveau de l'infrastructure - mappez votre propre nom de domaine sur le CDN, donnez-lui un court TTL, surveillez les fichiers sur le CDN (par exemple en utilisant Watchmouse ou autre), si CDN échoue, changez le DNS en site de sauvegarde.

Les autres options qui pourraient aider sont "mettre en cache pour toujours" sur le contenu statique, mais rien ne garantit que le navigateur les conservera bien sûr ou en utilisant le cache d'application.

En réalité, comme quelqu'un l'a dit en haut, si votre CDN n'est pas fiable, obtenez-en un nouveau

Andy

2
Andy Davies

On pourrait utiliser onerror pour cela:

<link rel="stylesheet" href="cdn.css" onerror="this.onerror=null;this.href='local.css';" />

Le this.onerror=null; est d'éviter les boucles sans fin au cas où le repli lui-même n'est pas disponible. Mais il pourrait également être utilisé pour avoir plusieurs solutions de rechange.

Cependant, cela ne fonctionne actuellement que dans Firefox et Chrome.

2
Jan

Regardez ces fonctions:

$.ajax({
    url:'CSS URL HERE',
    type:'HEAD',
    error: function()
    {
        AddLocalCss();
    },
    success: function()
    {
        //file exists
    }
});

Et voici la version JavaScript de Vanilla:

function UrlExists(url)
{
    var http = new XMLHttpRequest();
    http.open('HEAD', url, false);
    http.send();
    return http.status!=404;
}
if (!UrlExists('CSS URL HERE') {
AddLocalCss();
}

Maintenant, la fonction réelle:

function AddLocalCss(){
document.write('<link rel="stylesheet" type="text/css" href=" LOCAL CSS URL HERE">')
}

Assurez-vous simplement que AddLocalCss est appelé dans la tête.

Vous pouvez également envisager d'utiliser l'une des méthodes suivantes expliqué dans cette réponse :

Charger avec AJAX

$.get(myStylesLocation, function(css)
{
   $('<style type="text/css"></style>')
      .html(css)
      .appendTo("head");
});

Charger à l'aide créé dynamiquement

$('<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");

ou

$('<style type="text/css">@import url("' + myStylesLocation + '")</style>')
    .appendTo("head");
1
user529649