Как вернуться к локальной таблице стилей (не к сценарию), если 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,
Шон Маклин

Ответы:


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 я получаю rules = null, даже если он был загружен правильно. Я использую Chrome 26 и думаю, это потому, что скрипт перекрестный?
simplefuzz 05

8
Решение действительно не работает для все КДСА / Stylesheets, например CSSStyleSheetрасслоения плотных объектов , которые приходят из bootstrapcdn.com всех пустые rulesи cssRulesполе в моем браузере (Chrome 31). UPD : на самом деле это может быть проблема с несколькими доменами, файл css в ответе также у меня не работает.
Максим Ви.

3
Это тоже хорошее решение с использованием onerrorevent. 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
Дженни Теуниссен

2
К сожалению, мы не можем вводить наши собственные классы в файлы CDN. Может быть, попробовать использовать уже существующий.
Ахамед

1
Мне это ОЧЕНЬ нравится, спасибо. Это действительно очень мощно. Очевидно, я не могу манипулировать таблицей стилей CDN, но я знаю, какие классы используются, поэтому я изменил код, чтобы показать проверку, видны ли они - действительно очень умно :)
Носнибор

3
NB: Вы не действительно должны добавить новое правило для внешнего 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
Могу я спросить, в чем проблема, например, с использованием неэкранированных строк 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

в этой статье предлагаются некоторые решения для начальной загрузки 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» (если вы используете бесплатные шрифты). В противном случае все должно работать нормально.
Джейсон Кларк

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-адреса "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 на наличие междоменной таблицы стилей. Вы все еще можете использовать эту общую идею, но вам нужно выполнить другую проверку.
Джон Винопал

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

Есть ли способ просто проверить, а не загружать все это?
Шон Маклин,

Единственная возможная причина для выполнения запроса OP - это избежать избыточного сетевого трафика. Это создает избыточный сетевой трафик.
Стефан Кендалл,

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