Cara mengembalikan ke lembar gaya lokal (bukan skrip) jika CDN gagal

108

Saya menautkan ke stylesheet jQuery Mobile di CDN dan ingin kembali ke stylesheet versi lokal saya jika CDN gagal. Untuk skrip, solusinya terkenal:

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

Saya ingin melakukan sesuatu yang serupa untuk style sheet:

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

Saya tidak yakin apakah pendekatan serupa dapat dicapai karena saya tidak yakin apakah browser memblokir dengan cara yang sama saat menautkan skrip seperti saat memuat skrip (mungkin mungkin untuk memuat lembar gaya di tag skrip dan kemudian masukkan ke dalam halaman)?

Jadi pertanyaan saya adalah: Bagaimana cara memastikan stylesheet dimuat secara lokal jika CDN gagal?

ssn
sumber
2
Saya ingin tahu apakah ini mungkin juga ... Jika saya benar-benar khawatir tentang CDN yang turun, saya hanya akan menggunakan hosting lokal.
Fosco
2
@Stefan Kendall, menurut saya pernyataan yang tepat adalah bahwa situsnya kemungkinan besar akan turun daripada CDN
Shawn Mclean

Jawaban:

63

Tidak diuji lintas-browser tetapi saya pikir ini akan berhasil. Akan harus setelah Anda memuat jquery, atau Anda harus menulis ulang dalam Javascript biasa.

<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
sumber
solusi yang baik, satu masalah yang tidak teratasi adalah jika CDN terlalu lambat untuk dimuat ... mungkin semacam batas waktu?
GeorgeU
2
Untuk code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css , saya mendapatkan rules = null, meskipun telah dimuat dengan benar. Saya menggunakan Chrome 26 dan menurut saya itu karena skripnya lintas domain?
simplfuzz
8
Solusinya tidak benar-benar berfungsi untuk semua CDN / Stylesheets, misalnya CSSStyleSheetobjek js yang berasal dari bootstrapcdn.com semuanya kosong rulesdan cssRulesbidang di browser saya (Chrome 31). UPD : sebenarnya mungkin masalah lintas domain, file css di jawaban juga tidak berfungsi untuk saya.
Maksim Vi.
3
Ini juga soloution yang baik menggunakan onerroracara. stackoverflow.com/questions/30546795/…
Programmer Kimia
2
Apakah ini berfungsi untuk CSS yang dimuat dari domain lain? Tidak, Anda tidak dapat menghitung .rules/ .cssRulesuntuk lembar gaya eksternal. jsfiddle.net/E6yYN/13
Salman A
29

Saya kira pertanyaannya adalah untuk mendeteksi apakah stylesheet dimuat atau tidak. Salah satu pendekatan yang mungkin adalah sebagai berikut:

1) Tambahkan aturan khusus di akhir file CSS Anda, seperti:

#foo { display: none !important; }

2) Tambahkan div yang sesuai di HTML Anda:

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

3) Pada dokumen siap, periksa apakah #footerlihat atau tidak. Jika stylesheet dimuat, itu tidak akan terlihat.

Demo di sini - memuat tema kelancaran jquery-ui; tidak ada aturan yang ditambahkan ke stylesheet.

Salman A
sumber
42
1 untuk solusi cerdas. Satu-satunya masalah adalah, orang biasanya tidak bisa pergi dan menambahkan garis ke akhir lembar gaya yang dihosting di CDN seseorang
Jannie Theunissen
2
Sayangnya, kami tidak dapat mengetik di kelas kami sendiri ke file CDN. Mungkin kita bisa mencoba memanfaatkan yang sudah ada.
Malu
1
Saya sangat suka yang ini, terima kasih. Ini sangat kuat. Jelas saya tidak dapat memanipulasi stylesheet CDN tetapi saya tahu kelas apa yang digunakan jadi saya mengubah kode untuk menunjukkan cek apakah mereka terlihat - memang sangat pintar :)
Nosnibor
3
NB: Anda tidak benar-benar harus menambahkan aturan baru ke CSS eksternal. Cukup gunakan aturan yang ada yang perilakunya diketahui. Dalam demo saya, saya menggunakan kelas ui-helper-hidden yang seharusnya menyembunyikan elemen, saya kemudian memeriksa apakah elemen disembunyikan saat pemuatan halaman.
Salman A
28

Dengan asumsi Anda menggunakan CDN yang sama untuk css dan jQuery, mengapa tidak melakukan satu tes saja dan menangkap semuanya ??

<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
sumber
1
Bolehkah saya bertanya apa masalah menggunakan string tanpa konversi karakter pada awalnya, misalnya document.write("<script type='text/javascript' src='path/to/file.js'>")?
Jack Tuck
2
@JackTuck: Parser tidak dapat membedakan antara <script>di dalam string JS dan yang ditemukan di luar. Inilah alasan umum mengapa Anda juga melihat <\/script>saat menulis tag untuk penggantian CDN.
Brad Christie
6
-1 why not just do one test and catch it all?- Karena ada sejuta alasan mengapa seseorang mungkin gagal, dan yang lainnya berhasil.
Flimzy
7

artikel ini menyarankan beberapa solusi untuk bootstrap css http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

alternatifnya ini bekerja untuk 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
sumber
Bagi mereka yang ingin menggunakan ini dengan Font Awesome 5, Anda akan ingin mengubah 'FontAwesome' (di klausa if) menjadi 'Font Awesome 5 Free' (jika Anda menggunakan font gratis). Jika tidak, itu akan bekerja dengan baik.
Jason Clark
4

Anda mungkin dapat menguji keberadaan stylesheet di 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

Uji sesuatu yang spesifik untuk file CSS yang Anda gunakan.

Stefan Kendall
sumber
4

Seseorang dapat menggunakannya onerroruntuk itu:

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

Ini this.onerror=null;untuk menghindari loop tanpa akhir jika fallback itu sendiri tidak tersedia. Namun, ini juga dapat digunakan untuk memiliki beberapa fallback.

Namun, saat ini ini hanya berfungsi di Firefox dan Chrome.

Jan Martin Keil
sumber
3

Ini perpanjangan dari jawaban katy lavallee. Saya telah membungkus semuanya dalam sintaks fungsi yang dijalankan sendiri untuk mencegah tabrakan variabel. Saya juga membuat skrip tidak spesifik untuk satu tautan. IE, sekarang semua tautan lembar gaya dengan atribut url "data-fallback" akan diurai secara otomatis. Anda tidak perlu melakukan hard-code url ke dalam skrip ini seperti sebelumnya. Perhatikan bahwa ini harus dijalankan di akhir <head>elemen daripada di akhir <body>elemen, jika tidak maka dapat menyebabkan 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);
skibulk
sumber
1
Saya suka enkapsulasi, tetapi secara umum Anda tidak dapat memeriksa sheet.rules untuk stylesheet lintas domain. Anda masih dapat menggunakan gagasan umum ini tetapi perlu melakukan pemeriksaan yang berbeda.
John Vinopal
dengan document.styleSheets[i].ownerNode.datasetAnda dapat mengakses <link data-* />atribut
Alwin Kesler
2

Apakah Anda benar-benar ingin menggunakan rute javascript ini untuk memuat CSS jika CDN gagal?

Saya belum memikirkan semua implikasi kinerja tetapi Anda akan kehilangan kendali ketika CSS dimuat dan secara umum untuk kinerja pemuatan halaman, CSS adalah hal pertama yang ingin Anda unduh setelah HTML.

Mengapa tidak menangani ini di tingkat infrastruktur - petakan nama domain Anda sendiri ke CDN, berikan TTL singkat, pantau file di CDN (misalnya menggunakan Watchmouse atau yang lainnya), jika CDN gagal, ubah DNS ke situs cadangan.

Opsi lain yang mungkin membantu adalah "cache forever" pada konten statis, tetapi tidak ada jaminan browser akan menyimpannya atau menggunakan app-cache.

Pada kenyataannya seperti yang dikatakan seseorang di atas, jika CDN Anda tidak dapat diandalkan, dapatkan yang baru

Andy

Andy Davies
sumber
7
CDN dapat diandalkan, tetapi tidak untuk koneksi internet lingkungan pengembangan;)
icebreaker
1

Lihatlah fungsi-fungsi ini:

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

Dan inilah versi JavaScript 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();
}

Sekarang fungsi sebenarnya:

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

Pastikan AddLocalCss dipanggil di kepala.

Anda juga dapat mempertimbangkan untuk menggunakan salah satu cara berikut yang dijelaskan dalam jawaban ini :

Muat menggunakan AJAX

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

Muat menggunakan yang dibuat secara dinamis

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

atau

$('<style type="text/css">@import url("' + myStylesLocation + '")</style>')
    .appendTo("head");
Komunitas
sumber
Ya, setidaknya di browser modern, saya tidak yakin tentang IE6.
Apakah ada cara untuk memeriksa alih-alih mengunduh semuanya?
Shawn Mclean
Satu-satunya alasan yang mungkin untuk melakukan permintaan OP adalah untuk menghindari lalu lintas jaringan yang berlebihan. Ini menciptakan lalu lintas jaringan berlebih.
Stefan Kendall
@ Stefan Kendall: tidak, itu bahkan bukan alasan yang mungkin dia inginkan untuk memastikan bahwa file dimuat.
Jika itu satu-satunya perhatian, Anda tidak akan menggunakan CDN. Menguji header saja lebih baik, tetapi saya cukup yakin sebagian besar CDN dan browser Anda tidak akan mengizinkan XSS.
Stefan Kendall
-1

Saya mungkin akan menggunakan sesuatu seperti 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');
    }
  }
}]);

Diambil dari readme.

Ben Schwarz
sumber
10
@BenSchwarz, itu tidak berarti Anda dapat menempelkan beberapa kode yang tidak relevan yang sama sekali tidak menjawab pertanyaan yang diajukan.
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
sumber