วิธีการเปลี่ยนกลับไปใช้สไตล์ชีตภายใน (ไม่ใช่สคริปต์) หาก CDN ล้มเหลว


108

ฉันกำลังเชื่อมโยงกับสไตล์ชีต jQuery Mobile บน CDN และต้องการกลับไปใช้สไตล์ชีทเวอร์ชันท้องถิ่นของฉันหาก CDN ล้มเหลว สำหรับสคริปต์โซลูชันเป็นที่รู้จักกันดี:

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

ฉันต้องการทำสิ่งที่คล้ายกันสำหรับสไตล์ชีต:

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

ฉันไม่แน่ใจว่าจะสามารถใช้แนวทางที่คล้ายกันได้หรือไม่เพราะฉันไม่แน่ใจว่าเบราว์เซอร์บล็อกในลักษณะเดียวกันหรือไม่เมื่อเชื่อมโยงสคริปต์เหมือนกับที่ทำเมื่อโหลดสคริปต์ (อาจเป็นไปได้ที่จะโหลดสไตล์ชีตในแท็กสคริปต์แล้ว ฉีดเข้าไปในหน้า)?

ดังนั้นคำถามของฉันคือ: ฉันจะแน่ใจได้อย่างไรว่าสไตล์ชีตถูกโหลดในเครื่องหาก CDN ล้มเหลว


2
ฉันต้องการทราบว่าเป็นไปได้หรือไม่ ... ถ้าฉันรู้สึกไม่สบายใจเกี่ยวกับ CDN ที่หยุดทำงานฉันจะใช้โฮสติ้งภายใน
Fosco

2
@Stefan Kendall ฉันคิดว่าคำพูดที่ถูกต้องคือไซต์ของเขามีแนวโน้มที่จะลดลงมากกว่า CDN
Shawn Mclean

วิธีที่ดีที่สุด: stackoverflow.com/questions/26192897/…
nmit026

คำตอบ:


63

ไม่ได้ทดสอบข้ามเบราว์เซอร์ แต่ฉันคิดว่ามันจะได้ผล จะต้องหลังจากที่คุณโหลด jquery แล้วหรือคุณจะต้องเขียนใหม่ใน 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>

ทางออกที่ดีปัญหาหนึ่งที่ไม่สามารถแก้ไขได้คือถ้า CDN โหลดช้าเกินไป ... อาจจะหมดเวลาบ้าง?
GeorgeU

2
สำหรับcode.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.cssฉันได้รับกฎ = null แม้ว่าจะโหลดอย่างถูกต้อง ฉันใช้ Chrome 26 และฉันคิดว่าเป็นเพราะสคริปต์เป็นข้ามโดเมน?
simplfuzz

8
โซลูชันนี้ใช้ไม่ได้กับ CDNs / Stylesheets ทั้งหมดตัวอย่างเช่นCSSStyleSheetวัตถุ js ที่มาจาก bootstrapcdn.com ทั้งหมดว่างเปล่าrulesและcssRulesฟิลด์ในเบราว์เซอร์ของฉัน (Chrome 31) UPD : จริงๆแล้วอาจเป็นปัญหาข้ามโดเมนไฟล์ css ในคำตอบก็ใช้ไม่ได้สำหรับฉัน
Vi.

3
นี่เป็นวิธีแก้ปัญหาที่ดีโดยใช้onerrorเหตุการณ์ stackoverflow.com/questions/30546795/…
Chemical Programmer

2
ใช้งานได้กับ CSS ที่โหลดจากโดเมนอื่นหรือไม่ ไม่คุณไม่สามารถระบุ.rules/ .cssRulesสำหรับสไตล์ชีตภายนอกได้ jsfiddle.net/E6yYN/13
Salman A

29

ฉันเดาว่าคำถามคือการตรวจสอบว่ามีการโหลดสไตล์ชีตหรือไม่ แนวทางหนึ่งที่เป็นไปได้มีดังนี้:

1) เพิ่มกฎพิเศษที่ท้ายไฟล์ CSS ของคุณเช่น:

#foo { display: none !important; }

2) เพิ่ม div ที่เกี่ยวข้องใน HTML ของคุณ:

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

3) ในเอกสารพร้อมตรวจสอบว่า#fooมองเห็นได้หรือไม่ หากโหลดสไตล์ชีตแล้วจะมองไม่เห็น

สาธิตที่นี่ - โหลดธีมความเรียบ jquery-ui; ไม่มีการเพิ่มกฎในสไตล์ชีต


42
+1 เพื่อการแก้ปัญหาที่ชาญฉลาด ปัญหาเดียวคือปกติไม่สามารถไปและเพิ่มบรรทัดที่ส่วนท้ายของสไตล์ชีตที่โฮสต์บน CDN ของใครบางคน
Jannie Theunissen

2
ขออภัยเราไม่สามารถพิมพ์ในชั้นเรียนของเราเองเป็นไฟล์ CDN อาจเป็นไปได้ว่าเราสามารถลองใช้ประโยชน์จากสิ่งที่มีอยู่แล้ว
Ahamed

1
ฉันชอบอันนี้มากขอบคุณ มันค่อนข้างทรงพลังจริงๆ เห็นได้ชัดว่าฉันไม่สามารถจัดการสไตล์ชีท CDN ได้ แต่ฉันรู้ว่ากำลังใช้คลาสอะไรอยู่ดังนั้นฉันจึงแก้ไขโค้ดเพื่อแสดงว่ามองเห็นได้หรือไม่ - ฉลาดมากจริงๆ :)
Nosnibor

3
หมายเหตุ: คุณไม่ได้จริงๆต้องเพิ่มกฎใหม่เพื่อ CSS ภายนอก เพียงใช้กฎที่มีอยู่ซึ่งทราบพฤติกรรม ในการสาธิตของฉันฉันใช้คลาส ui-helper-hidden ซึ่งควรจะซ่อนองค์ประกอบจากนั้นฉันตรวจสอบว่าองค์ประกอบนั้นซ่อนอยู่ในการโหลดหน้าหรือไม่
Salman A

28

สมมติว่าคุณใช้ CDN เดียวกันสำหรับ css และ jQuery ทำไมไม่ทำการทดสอบเพียงครั้งเดียวและตรวจจับทั้งหมด ??

<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
ฉันขอถามปัญหาเกี่ยวกับการใช้สตริงที่ไม่ใช้ Escape ในตอนแรกเช่นdocument.write("<script type='text/javascript' src='path/to/file.js'>")?
Jack Tuck

2
@JackTuck: ตัวแยกวิเคราะห์ไม่สามารถแยกความแตกต่างระหว่าง<script>ภายในสตริง JS กับสตริงที่พบภายนอก นี่คือสาเหตุที่คุณเห็น<\/script>เมื่อเขียนแท็กสำหรับ CDN fallbacks
Brad Christie

6
-1 why not just do one test and catch it all?- เพราะมีหลายล้านเหตุผลที่อาจล้มเหลวและคนอื่น ๆ ประสบความสำเร็จ
Flimzy

7

บทความนี้แนะนำวิธีแก้ปัญหาสำหรับ bootstrap css http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

หรือวิธีนี้ใช้ได้กับ 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>

สำหรับผู้ที่ต้องการใช้สิ่งนี้กับ Font Awesome 5 คุณจะต้องเปลี่ยน 'FontAwesome' (ในส่วนคำสั่ง if) เป็น 'Font Awesome 5 Free' (หากคุณใช้แบบอักษรฟรี) มิฉะนั้นควรใช้งานได้ดี
Jason Clark

4

คุณอาจทดสอบการมีอยู่ของสไตล์ชีตใน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

ทดสอบสิ่งที่เฉพาะเจาะจงสำหรับไฟล์ CSS ที่คุณใช้


4

สามารถใช้onerrorสำหรับสิ่งนั้น:

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

this.onerror=null;คือการหลีกเลี่ยงลูปไม่มีที่สิ้นสุดในกรณีที่ fallback ตัวเองไม่สามารถใช้ได้ แต่ยังสามารถใช้เพื่อให้มีทางเลือกหลายรายการ

อย่างไรก็ตามขณะนี้ใช้ได้เฉพาะใน Firefox และ Chrome เท่านั้น


3

นี่คือส่วนเสริมสำหรับคำตอบของ katy lavallee ฉันได้รวมทุกอย่างไว้ในไวยากรณ์ของฟังก์ชันที่ดำเนินการเองเพื่อป้องกันการชนกันของตัวแปร ฉันยังทำให้สคริปต์ไม่เฉพาะเจาะจงกับลิงก์เดียว IE ตอนนี้ลิงก์สไตล์ชีตที่มีแอตทริบิวต์ url "data-fallback" จะถูกแยกวิเคราะห์โดยอัตโนมัติ คุณไม่จำเป็นต้องฮาร์ดโค้ด URL ลงในสคริปต์นี้เหมือนเมื่อก่อน โปรดทราบว่าควรเรียกใช้ที่ส่วนท้ายของ<head>องค์ประกอบมากกว่าที่ส่วนท้ายของ<body>องค์ประกอบมิฉะนั้นอาจทำให้เกิด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
ฉันชอบการห่อหุ้ม แต่โดยทั่วไปคุณไม่สามารถตรวจสอบ sheet.rules สำหรับสไตล์ชีตข้ามโดเมนได้ คุณยังสามารถใช้แนวคิดทั่วไปนี้ได้ แต่ต้องตรวจสอบแบบอื่น
John Vinopal

ด้วยdocument.styleSheets[i].ownerNode.datasetคุณสามารถเข้าถึง<link data-* />แอตทริบิวต์
Alwin Kesler

2

คุณต้องการลงเส้นทางจาวาสคริปต์นี้เพื่อโหลด CSS ในกรณีที่ CDN ล้มเหลวหรือไม่?

ฉันไม่ได้คิดถึงผลกระทบด้านประสิทธิภาพทั้งหมด แต่คุณจะสูญเสียการควบคุมเมื่อโหลด CSS และโดยทั่วไปสำหรับประสิทธิภาพการโหลดหน้า CSS เป็นสิ่งแรกที่คุณต้องการดาวน์โหลดหลังจาก HTML

ทำไมไม่จัดการสิ่งนี้ในระดับโครงสร้างพื้นฐาน - แมปชื่อโดเมนของคุณเองกับ CDN ให้ TTL สั้น ๆ ตรวจสอบไฟล์บน CDN (เช่นใช้ Watchmouse หรืออย่างอื่น) หาก CDN ล้มเหลวให้เปลี่ยน DNS เป็นไซต์สำรอง

ตัวเลือกอื่น ๆ ที่อาจช่วยได้คือ "แคชตลอดไป" ในเนื้อหาคงที่ แต่ไม่มีการรับประกันว่าเบราว์เซอร์จะเก็บไว้แน่นอนหรือใช้แอปแคช

ในความเป็นจริงตามที่มีคนพูดไว้ด้านบนถ้า CDN ของคุณไม่น่าเชื่อถือให้รับอันใหม่

แอนดี้


7
CDN สามารถเชื่อถือได้ แต่ไม่ใช่การเชื่อมต่ออินเทอร์เน็ตในสภาพแวดล้อมการพัฒนา)
เรือตัดน้ำแข็ง

1

ดูฟังก์ชั่นเหล่านี้:

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

และนี่คือเวอร์ชัน JavaScript ของวานิลลา:

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

ตอนนี้ฟังก์ชั่นจริง:

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

ตรวจสอบให้แน่ใจว่ามีการเรียก AddLocalCss ในหัว

คุณอาจลองใช้หนึ่งในวิธีต่อไปนี้ที่อธิบายไว้ในคำตอบนี้ :

โหลดโดยใช้ AJAX

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

โหลดโดยใช้ที่สร้างขึ้นแบบไดนามิก

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

หรือ

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

ใช่อย่างน้อยที่สุดในเบราว์เซอร์สมัยใหม่ฉันไม่แน่ใจเกี่ยวกับ IE6

มีวิธีตรวจสอบแทนการดาวน์โหลดทั้งหมดหรือไม่?
Shawn Mclean

เหตุผลเดียวที่เป็นไปได้ในการร้องขอ OPs คือการหลีกเลี่ยงปริมาณการใช้งานเครือข่ายที่มากเกินไป ซึ่งจะสร้างปริมาณการใช้งานเครือข่ายส่วนเกิน
Stefan Kendall

@stefan Kendall: ไม่นั่นไม่ใช่เหตุผลที่เป็นไปได้ที่เขาต้องการให้แน่ใจว่าไฟล์ถูกโหลด

หากนั่นเป็นเพียงข้อกังวลเดียวคุณจะไม่ใช้ CDN การทดสอบเฉพาะส่วนหัวจะดีกว่า แต่ฉันค่อนข้างมั่นใจว่า CDN ส่วนใหญ่และเบราว์เซอร์ของคุณจะไม่อนุญาตให้ใช้ XSS
Stefan Kendall

-1

ฉันอาจจะใช้บางอย่างเช่น 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');
    }
  }
}]);

นำมาจากreadme.


10
@BenSchwarz นั่นไม่ได้หมายความว่าคุณสามารถวางโค้ดที่ไม่เกี่ยวข้องซึ่งจะไม่ตอบคำถามที่ถามได้
AlicanC

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.