Zurückgreifen auf ein lokales Stylesheet (kein Skript), wenn CDN fehlschlägt

108

Ich verlinke auf einem CDN mit dem jQuery Mobile-Stylesheet und möchte auf meine lokale Version des Stylesheets zurückgreifen, wenn das CDN fehlschlägt. Für Skripte ist die Lösung bekannt:

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

Ich möchte etwas Ähnliches für ein Stylesheet tun:

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

Ich bin nicht sicher, ob ein ähnlicher Ansatz erreicht werden kann, da ich nicht sicher bin, ob der Browser beim Verknüpfen eines Skripts genauso blockiert wie beim Laden eines Skripts (möglicherweise ist es möglich, ein Stylesheet in ein Skript-Tag zu laden und dann in die Seite injizieren)?

Meine Frage lautet also: Wie stelle ich sicher, dass ein Stylesheet lokal geladen wird, wenn ein CDN ausfällt?

ssn
quelle
2
Ich würde gerne wissen, ob dies auch möglich ist ... Wenn ich mir wirklich Sorgen darüber machen würde, dass das CDN nicht funktioniert, würde ich nur lokales Hosting verwenden.
Fosco
2
@Stefan Kendall, ich denke, die richtige Aussage ist, dass seine Website mehr als wahrscheinlich als ein CDN ausfallen wird
Shawn Mclean

Antworten:

63

Nicht browserübergreifend getestet, aber ich denke, das wird funktionieren. Muss aber sein, nachdem Sie jquery geladen haben, oder Sie müssen es in einfachem Javascript umschreiben.

<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>
katy lavallee
quelle
Gute Lösung, ein Problem, das nicht behoben wird, ist, wenn das CDN viel zu langsam zum Laden ist ... vielleicht eine Art Zeitüberschreitung?
GeorgeU
2
Für code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css erhalte ich rules = null, obwohl es ordnungsgemäß geladen wurde. Ich verwende Chrome 26 und denke, das liegt daran, dass das Skript domänenübergreifend ist.
simplfuzz
8
Die Lösung funktioniert nicht wirklich für alle CDNs / Stylesheets, zum Beispiel CSSStyleSheetjs Objekte , die von bootstrapcdn.com kommen alle haben leer rulesund cssRulesFelder in meinem Browser (Chrome 31). UPD : Es könnte tatsächlich ein domänenübergreifendes Problem sein. Die CSS-Datei in der Antwort funktioniert auch bei mir nicht.
Maksim Vi.
3
Dies ist auch eine gute Lösung mit onerrorevent. stackoverflow.com/questions/30546795/…
Chemischer Programmierer
2
Funktioniert es für CSS, das von einer anderen Domain geladen wurde? Nein, Sie können .rules/ .cssRulesfür externe Stylesheets nicht aufzählen . jsfiddle.net/E6yYN/13
Salman A
29

Ich denke, die Frage ist, ob ein Stylesheet geladen ist oder nicht. Ein möglicher Ansatz ist wie folgt:

1) Fügen Sie am Ende Ihrer CSS-Datei eine spezielle Regel hinzu, z.

#foo { display: none !important; }

2) Fügen Sie das entsprechende div in Ihr HTML ein:

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

3) Überprüfen Sie bei bereitem Dokument, ob #fooes sichtbar ist oder nicht. Wenn das Stylesheet geladen wurde, ist es nicht sichtbar.

Demo hier - lädt jquery-ui Glätte Thema; Dem Stylesheet wird keine Regel hinzugefügt.

Salman A.
quelle
42
+1 für eine clevere Lösung. Das einzige Problem ist, dass man normalerweise keine Zeile am Ende eines Stylesheets einfügen kann, das auf dem CDN einer anderen Person gehostet wird
Jannie Theunissen
2
Leider können wir unsere eigenen Klassen nicht in CDN-Dateien eingeben. Vielleicht können wir versuchen, die bereits vorhandene zu nutzen.
Ahamed
1
Ich mag dieses sehr, danke. Es ist wirklich ziemlich mächtig. Natürlich kann ich das CDN-Stylesheet nicht manipulieren, aber ich weiß, welche Klassen verwendet werden, also habe ich den Code geändert, um zu überprüfen, ob sie sichtbar sind - in der Tat sehr clever :)
Nosnibor
3
NB: Sie müssen dem externen CSS nicht wirklich eine neue Regel hinzufügen. Verwenden Sie einfach eine vorhandene Regel, deren Verhalten bekannt ist. In meiner Demo verwende ich die Klasse ui-helper-hidden, die das Element ausblenden soll. Dann überprüfe ich, ob das Element beim Laden der Seite ausgeblendet wird.
Salman A
28

Angenommen, Sie verwenden dasselbe CDN für CSS und jQuery, warum nicht einfach einen Test durchführen und alles abfangen?

<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>
Mike Wills
quelle
1
Darf ich fragen, was das Problem mit der anfänglichen Verwendung von nicht entkappten Zeichenfolgen ist, z document.write("<script type='text/javascript' src='path/to/file.js'>").
Jack Tuck
2
@ JackTuck: Der Parser kann nicht zwischen <script>einer JS-Zeichenfolge und einer außerhalb unterscheiden. Dies ist häufig der Grund, warum Sie dies auch <\/script>beim Schreiben von Tags für CDN-Fallbacks sehen.
Brad Christie
6
-1 why not just do one test and catch it all?- Weil es eine Million Gründe gibt, warum einer scheitern könnte und die anderen erfolgreich sind.
Flimzy
7

Dieser Artikel schlägt einige Lösungen für das Bootstrap-CSS vor. http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

alternativ funktioniert dies für 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="https://stackoverflow.com/css/font-awesome.min.css" rel="stylesheet">');
        }
        $span.remove();
    })(jQuery);
</script>
dc2009
quelle
Wenn Sie dies mit Font Awesome 5 verwenden möchten, sollten Sie "FontAwesome" (in der if-Klausel) in "Font Awesome 5 Free" (wenn Sie die kostenlosen Schriftarten verwenden) ändern. Ansonsten sollte es gut funktionieren.
Jason Clark
4

Möglicherweise können Sie die Existenz des Stylesheets in testen 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

Testen Sie, ob die CSS-Datei, die Sie verwenden, spezifisch ist.

Stefan Kendall
quelle
4

Man könnte dafür verwenden onerror:

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

Dies this.onerror=null;dient dazu, Endlosschleifen zu vermeiden, falls der Fallback selbst nicht verfügbar ist. Es könnte aber auch verwendet werden, um mehrere Fallbacks zu haben.

Dies funktioniert derzeit jedoch nur in Firefox und Chrome.

Jan Martin Keil
quelle
3

Hier ist eine Erweiterung der Antwort von Katy Lavallee. Ich habe alles in eine selbstausführende Funktionssyntax eingeschlossen, um variable Kollisionen zu verhindern. Ich habe das Skript auch unspezifisch für einen einzelnen Link gemacht. IE, jetzt wird jeder Stylesheet-Link mit einem URL-Attribut "Daten-Fallback" automatisch analysiert. Sie müssen die URLs nicht wie zuvor in dieses Skript fest codieren. Beachten Sie, dass dies am Ende des <head>Elements und nicht am Ende des <body>Elements ausgeführt werden sollte, da dies sonst zu FOUC führen kann .

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);
Skibulk
quelle
1
Ich mag die Kapselung, aber im Allgemeinen können Sie sheet.rules nicht auf ein domänenübergreifendes Stylesheet untersuchen. Sie können diese allgemeine Idee weiterhin verwenden, müssen jedoch eine andere Überprüfung durchführen.
John Vinopal
mit können document.styleSheets[i].ownerNode.datasetSie auf <link data-* />Attribute zugreifen
Alwin Kesler
2

Möchten Sie wirklich diesen Javascript-Weg gehen, um CSS zu laden, falls ein CDN ausfällt?

Ich habe nicht alle Auswirkungen auf die Leistung durchdacht, aber Sie verlieren die Kontrolle darüber, wann das CSS geladen wird. Im Allgemeinen ist CSS für die Leistung beim Laden von Seiten das erste, was Sie nach dem HTML-Code herunterladen möchten.

Warum nicht auf Infrastrukturebene damit umgehen? Ordnen Sie dem CDN Ihren eigenen Domainnamen zu, geben Sie ihm eine kurze TTL, überwachen Sie die Dateien auf dem CDN (z. B. mit Watchmouse oder etwas anderem). Wenn das CDN fehlschlägt, ändern Sie den DNS in eine Sicherungssite.

Andere Optionen, die helfen könnten, sind "für immer zwischenspeichern" für statische Inhalte, aber es gibt keine Garantie, dass der Browser sie natürlich behält oder den App-Cache verwendet.

In Wirklichkeit, wie jemand oben sagte, wenn Ihr CDN unzuverlässig ist, besorgen Sie sich ein neues

Andy

Andy Davies
quelle
7
CDN kann zuverlässig sein, aber nicht die Internetumgebung der Entwicklungsumgebung;)
Eisbrecher
1

Schauen Sie sich diese Funktionen an:

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

Und hier ist Vanille 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();
}

Nun die eigentliche Funktion:

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

Stellen Sie einfach sicher, dass AddLocalCss im Kopf aufgerufen wird.

Sie können auch eine der folgenden in dieser Antwort erläuterten Methoden verwenden :

Laden Sie mit AJAX

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

Laden Sie mit dynamisch erstellten

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

oder

$('<style type="text/css">@import url("' + myStylesLocation + '")</style>')
    .appendTo("head");
Gemeinschaft
quelle
Ja, zumindest in modernen Browsern bin ich mir bei IE6 nicht sicher.
Gibt es eine Möglichkeit, nur zu überprüfen, anstatt das Ganze herunterzuladen?
Shawn Mclean
Der einzig mögliche Grund für die OPs-Anforderung besteht darin, übermäßigen Netzwerkverkehr zu vermeiden. Dies führt zu übermäßigem Netzwerkverkehr.
Stefan Kendall
@stefan Kendall: Nein, das ist nicht einmal der mögliche Grund, warum er sicherstellen will, dass die Dateien geladen werden.
Wenn dies das einzige Problem wäre, würden Sie einfach kein CDN verwenden. Es ist besser, nur den Header zu testen, aber ich bin mir ziemlich sicher, dass die meisten CDNs und Ihr Browser XSS nicht zulassen.
Stefan Kendall
-1

Ich würde wahrscheinlich so etwas wie yepnope.js verwenden

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');
    }
  }
}]);

Aus der Readme-Datei entnommen .

Ben Schwarz
quelle
10
@ BenSchwarz, das heißt nicht, dass Sie irrelevanten Code einfügen können, der die gestellte Frage in keiner Weise beantwortet.
AlicanC
-8
//(load your cdn lib here first)

<script>window.jQuery || document.write("<script src='//me.com/path/jquery-1.x.min.js'>\x3C/script>")</script>
crazy4groovy
quelle