Як відновлювати місцевий таблицю стилів (не сценарій), якщо 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 не працює, я б просто застосував локальний хостинг.
Фоско

2
@ Стефан Кендалл, я думаю, що правильне твердження полягає в тому, що його сайт швидше за все знизиться, ніж CDN
Шон Мклін

Відповіді:


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
Рішення насправді не працює для всіх CDN / Stylesheets, наприклад, CSSStyleSheetjs-об’єкти, що надходять з bootstrapcdn.com, у моєму браузері порожні rulesта cssRulesполя (Chrome 31). UPD : це насправді може бути проблема перехресного домену, файл css у відповіді також не працює для мене.
Максим Ві.

3
Це також добре рішення, використовуючи onerrorподію. stackoverflow.com/questions/30546795/…
програміст з хімічних речовин

2
Чи працює це для CSS, завантаженого з іншого домену? Ні, ви не можете перерахувати .rules/ .cssRulesдля зовнішніх таблиць стилів. jsfiddle.net/E6yYN/13
Салман

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. Можливо, ми можемо спробувати використати ту, яка вже існує.
Соромно

1
Мені подобається цей ЛОТ, дякую. Це справді досить потужно. Очевидно, що я не можу маніпулювати таблицею стилів CDN, але я знаю, які класи використовуються, тому я змінив код, щоб перевірити, чи вони видимі - дуже розумні дійсно :)
Nosnibor

3
Примітка: вам дійсно не потрібно додавати нове правило до зовнішнього CSS. Просто використовуйте існуюче правило, поведінка якого відома. У моєму демонстрації я використовую прихований клас ui-helper, який повинен приховувати елемент, а потім перевіряю, чи елемент прихований при завантаженні сторінки.
Салман

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
Чи можу я запитати, у чому полягає проблема із початком використання нерозмірних рядків, наприклад document.write("<script type='text/javascript' src='path/to/file.js'>")?
Джек Так

2
@JackTuck: Аналізатор не може розмежовувати <script>внутрішній рядок JS та той, що знаходиться зовні. Це зазвичай є причиною того, що ви також бачите <\/script>при написанні тегів для резервних копій CDN.
Бред Крісті

6
-1 why not just do one test and catch it all?- Тому що є мільйон причин, що один може зазнати невдачі, а інші досягти успіху.
Flimzy

7

Ця стаття пропонує деякі рішення для bossstrap css http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

Альтернативно це працює на користь

<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" (якщо ви використовуєте безкоштовні шрифти). В іншому випадку це повинно працювати добре.
Джейсон Кларк

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;, Щоб уникнути зациклення в разі запасний варіант це сам поза зоною досяжності. Але це також може бути використане для декількох резервних копій.

Однак наразі це працює лише у Firefox та Chrome.


3

Ось розширення до відповіді Кеті Лаваллі. Я загорнув усе в синтаксис функції самовиконання, щоб запобігти змінним зіткненням. Я також зробив сценарій неспецифічним для одного посилання. IE, тепер будь-яке посилання таблиці аркушів стилів з атрибутом URL-адреси "западання даних" буде автоматично проаналізовано. Вам не доведеться жорстко кодувати 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 для таблиці міждоменних стилів. Ви все ще можете використовувати цю загальну ідею, але потрібно зробити іншу перевірку.
Іван Вінопаль

з document.styleSheets[i].ownerNode.datasetви можете отримати доступ <link data-* />атрибути
Елвін Кеслер

2

Ви дійсно хочете піти по цьому маршруту javascript, щоб завантажити 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.

Чи є спосіб просто перевірити замість того, щоб завантажувати всю річ?
Шон Мклін

Єдиною можливою причиною для запиту ОП є уникнення зайвого мережевого трафіку. Це створює надлишковий мережевий трафік.
Стефан Кендалл

@stefan Kendall: ні, це навіть не можлива причина, щоб він хотів переконатися, що файли завантажуються.

Якби це було єдиним питанням, ви б просто не використовували CDN. Тестування лише заголовка краще, але я впевнений, що більшість CDN та ваш веб-переглядач не дозволяють використовувати XSS.
Стефан Кендалл

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

Взято з ритму.


10
@BenSchwarz, це не означає, що ви можете вставити невідповідний код, який жодним чином не відповідає на задане питання.
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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.