Cách dự phòng cho biểu định kiểu cục bộ (không phải tập lệnh) nếu CDN không thành công


108

Tôi đang liên kết với biểu định kiểu jQuery Mobile trên CDN và muốn quay lại phiên bản định kiểu cục bộ của tôi nếu CDN không thành công. Đối với tập lệnh, giải pháp nổi tiếng là:

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

Tôi muốn làm một cái gì đó tương tự cho một style sheet:

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

Tôi không chắc liệu có thể đạt được cách tiếp cận tương tự hay không vì tôi không chắc liệu trình duyệt có chặn theo cách tương tự khi liên kết tập lệnh như khi tải tập lệnh hay không (có thể có thể tải biểu định kiểu trong thẻ tập lệnh và sau đó tiêm nó vào trang)?

Vì vậy, câu hỏi của tôi là: Làm cách nào để đảm bảo một biểu định kiểu được tải cục bộ nếu CDN bị lỗi?


2
Tôi cũng muốn biết liệu điều này có khả thi không ... Nếu tôi thực sự lo lắng về việc CDN không hoạt động, tôi sẽ chỉ sử dụng dịch vụ lưu trữ cục bộ.
Fosco

2
@Stefan Kendall, tôi nghĩ rằng những tuyên bố đúng là trang web của mình sẽ nhiều hơn khả năng đi xuống hơn một CDN
Shawn Mclean

Câu trả lời:


63

Không được thử nghiệm trên nhiều trình duyệt nhưng tôi nghĩ điều này sẽ hoạt động. Tuy nhiên, sẽ phải sau khi bạn tải jquery, hoặc bạn sẽ phải viết lại nó bằng Javascript thuần túy.

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

giải pháp tốt, một vấn đề mà nó không giải quyết là nếu CDN quá chậm để tải ... có thể một số loại thời gian chờ?
GeorgeU

2
Đối với code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css , tôi nhận được quy tắc = null, mặc dù nó đã được tải đúng cách. Tôi đang sử dụng Chrome 26 và tôi nghĩ đó là do tập lệnh là tên miền chéo?
simplfuzz

8
Giải pháp không thực sự hoạt động cho tất cả CDN / Stylesheets, ví dụ: CSSStyleSheetcác đối tượng js đến từ bootstrapcdn.com đều có trường rulesvà trống cssRulestrong trình duyệt của tôi (Chrome 31). UPD : nó thực sự có thể là một vấn đề tên miền chéo, tệp css trong câu trả lời cũng không hoạt động với tôi.
Maksim Vi.

3
Đây cũng là một onerrorsự kiện solo tốt . stackoverflow.com/questions/30546795/…
Lập trình viên hóa học

2
Nó có hoạt động với CSS được tải từ miền khác không? Không, bạn không thể liệt kê .rules/ .cssRulescho các bảng định kiểu bên ngoài. jsfiddle.net/E6yYN/13
Salman A

29

Tôi đoán câu hỏi là để phát hiện xem một biểu định kiểu đã được tải hay chưa. Một cách tiếp cận có thể thực hiện như sau:

1) Thêm quy tắc đặc biệt vào cuối tệp CSS của bạn, như:

#foo { display: none !important; }

2) Thêm div tương ứng trong HTML của bạn:

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

3) Trên tài liệu đã sẵn sàng, hãy kiểm tra xem #foocó hiển thị hay không. Nếu biểu định kiểu đã được tải, nó sẽ không hiển thị.

Demo tại đây - tải chủ đề mượt mà jquery-ui; không có quy tắc nào được thêm vào biểu định kiểu.


42
+1 cho một giải pháp thông minh. Chỉ có vấn đề là, người ta thường không thể đi và thêm một dòng vào cuối một style sheet được lưu trữ trên CDN của một ai đó
Jannie Theunissen

2
Rất tiếc, chúng tôi không thể nhập các lớp của riêng mình vào tệp CDN. Có thể chúng ta có thể cố gắng sử dụng cái đã tồn tại.
Ahamed

1
Tôi thích cái này RẤT NHIỀU, cảm ơn bạn. Nó thực sự khá mạnh mẽ. Rõ ràng là tôi không thể thao tác bảng định kiểu CDN nhưng tôi biết những lớp nào đang được sử dụng vì vậy tôi đã sửa đổi mã để hiển thị kiểm tra xem chúng có hiển thị hay không - thực sự rất thông minh :)
Nosnibor

3
NB: bạn không thực sự phải thêm quy tắc mới vào CSS bên ngoài. Chỉ cần sử dụng một quy tắc hiện có mà hành vi của nó đã biết. Trong bản trình diễn của mình, tôi sử dụng ui-helper-hidden class được cho là ẩn phần tử, sau đó tôi kiểm tra xem phần tử có bị ẩn khi tải trang hay không.
Salman A

28

Giả sử bạn đang sử dụng cùng một CDN cho css và jQuery, tại sao không chỉ làm một bài kiểm tra và bắt tất cả ??

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

1
Tôi có thể hỏi vấn đề với việc sử dụng chuỗi không thoát ban đầu là document.write("<script type='text/javascript' src='path/to/file.js'>")gì không?
Jack Tuck:

2
@JackTuck: Trình phân tích cú pháp không thể phân biệt giữa <script>chuỗi JS bên trong và chuỗi được tìm thấy bên ngoài. Đây thường là lý do tại sao bạn cũng thấy <\/script>khi viết ra các thẻ cho CDN dự phòng.
Brad Christie

6
-1 why not just do one test and catch it all?- Bởi vì có hàng triệu lý do khiến một người có thể thất bại và những lý do khác lại thành công.
Flimzy

7

bài viết này gợi ý một số giải pháp cho css bootstrap http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

cách khác, điều này hoạt động cho phông chữ tuyệt vời

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

Đối với những người muốn sử dụng tính năng này với Font Awesome 5, bạn sẽ muốn thay đổi 'FontAwesome' (trong mệnh đề if) thành 'Font Awesome 5 Free' (nếu bạn đang sử dụng các phông chữ miễn phí). Nếu không, nó sẽ hoạt động tốt.
Jason Clark

4

Bạn thể kiểm tra sự tồn tại của biểu định kiểu trong 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

Kiểm tra một cái gì đó cụ thể cho tệp CSS bạn đang sử dụng.


4

Người ta có thể sử dụng onerrorcho điều đó:

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

Các this.onerror=null;là để tránh các vòng lặp vô tận trong trường hợp dự phòng tự nó là không có sẵn. Nhưng nó cũng có thể được sử dụng để có nhiều dự phòng.

Tuy nhiên, điều này hiện chỉ hoạt động trên Firefox và Chrome.


3

Đây là phần mở rộng cho câu trả lời của katy lavallee. Tôi đã gói mọi thứ trong cú pháp hàm tự thực thi để ngăn chặn các xung đột biến. Tôi cũng đã làm cho tập lệnh không cụ thể cho một liên kết. IE, bây giờ bất kỳ liên kết biểu định kiểu nào có thuộc tính url "data-dự phòng" sẽ tự động được phân tích cú pháp. Bạn không cần phải mã hóa cứng các url vào tập lệnh này như trước đây. Lưu ý rằng điều này nên được chạy ở cuối <head>phần tử chứ không phải ở cuối <body>phần tử, nếu không nó có thể gây ra 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);

1
Tôi thích sự đóng gói, nhưng nói chung bạn không thể kiểm tra sheet.rules cho một biểu định kiểu tên miền chéo. Bạn vẫn có thể sử dụng ý tưởng chung này nhưng cần thực hiện kiểm tra khác.
John Vinopal

với document.styleSheets[i].ownerNode.datasetbạn có thể truy cập vào <link data-* />các thuộc tính
Alwin Kesler

2

Bạn có thực sự muốn đi xuống tuyến đường javascript này để tải CSS trong trường hợp CDN bị lỗi không?

Tôi chưa nghĩ hết tác động của hiệu suất nhưng bạn sẽ mất kiểm soát khi CSS được tải và nói chung đối với hiệu suất tải trang, CSS là thứ đầu tiên bạn muốn tải xuống sau HTML.

Tại sao không xử lý điều này ở cấp cơ sở hạ tầng - ánh xạ tên miền của riêng bạn với CDN, đặt cho nó một TTL ngắn, theo dõi các tệp trên CDN (ví dụ: sử dụng Watchmouse hoặc thứ gì đó khác), nếu CDN bị lỗi, hãy thay đổi DNS thành trang web sao lưu.

Các tùy chọn khác có thể hữu ích là "bộ nhớ cache vĩnh viễn" trên nội dung tĩnh nhưng không có gì đảm bảo rằng trình duyệt sẽ giữ chúng tất nhiên hoặc sử dụng bộ đệm ứng dụng.

Trong thực tế như ai đó đã nói ở trên, nếu CDN của bạn không đáng tin cậy, hãy lấy một cái mới

Andy


7
CDN có thể đáng tin cậy, nhưng không phải là kết nối internet trong môi trường phát triển;)
icebreaker

1

Nhìn vào các chức năng sau:

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

Và đây là phiên bản JavaScript vani:

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

Bây giờ chức năng thực tế:

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

Chỉ cần đảm bảo rằng AddLocalCss được gọi trong đầu.

Bạn cũng có thể cân nhắc sử dụng một trong những cách sau giải thích trong câu trả lời này :

Tải bằng AJAX

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

Tải bằng cách tạo động

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

hoặc là

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

Vâng, ít nhất là trong trình duyệt hiện đại, tôi không chắc về IE6.

Có cách nào để chỉ kiểm tra thay vì tải xuống toàn bộ không?
Shawn Mclean,

Lý do duy nhất có thể để thực hiện yêu cầu OP là để tránh vượt quá lưu lượng mạng. Điều này tạo ra lưu lượng mạng dư thừa.
Stefan Kendall

@stefan Kendall: không, đó thậm chí không phải là lý do có thể khiến anh ấy muốn đảm bảo rằng các tệp được tải.

Nếu đó là mối quan tâm duy nhất, bạn sẽ không sử dụng CDN. Chỉ kiểm tra tiêu đề thì tốt hơn, nhưng tôi khá chắc chắn rằng hầu hết các CDN và trình duyệt của bạn sẽ không cho phép XSS.
Stefan Kendall

-1

Tôi có thể sẽ sử dụng một cái gì đó như 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');
    }
  }
}]);

Lấy từ readme.


10
@BenSchwarz, điều đó không có nghĩa là bạn có thể dán một số mã không liên quan mà không có cách nào trả lời câu hỏi được hỏi.
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>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.