2013-07-19 20 views
8

Mam pewien kod (to nie jest mój, ale biblioteka SlickGrid), który tworzy element <style>, wstawia go do DOM, a następnie natychmiast próbuje znaleźć nowy arkusz stylów w kolekcji document.styleSheets.Webkit - dynamicznie tworzony arkusz stylów - kiedy naprawdę się ładuje?

W pakiecie WebKit czasami się to nie udaje. W rzeczywistości nie mam pojęcia, jakie są okoliczności, ale to nic, co jest konsekwentnie powtarzalne. Pomyślałem mogłem obejść poprzez zmianę kodu tak czek dla obiektu StyleSheet nie zdarza się aż przypadku load na element stylu, tak jak poniżej:

$style = $("<style type='text/css' rel='stylesheet' />").appendTo($("head")); 
    var rules = ...;// code to create the text of the rules here 
    if ($style[0].styleSheet) { // IE 
    $style[0].styleSheet.cssText = rules.join(" "); 
    } else { 
    $style[0].appendChild(document.createTextNode(rules.join(" "))); 
    } 
    $style.bind('load', function() { 
      functionThatExpectsTheStylesheet(); 
    }); 

i functionThatExpectsTheStylesheet próbuje zlokalizować rzeczywisty obiekt stylesheet tak:

var sheets = document.styleSheets; 
    for (var i = 0; i < sheets.length; i++) { 
     if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) { 
     stylesheet = sheets[i]; 
     break; 
     } 
    } 

, ale czasami nawet w tym miejscu nie znaleziono obiektu arkusza stylów.

Więc moje pytanie brzmi:

  1. Czy zdarzenie w rzeczywistości load nie gwarantuje, że obiekt styleSheet będzie dostępna? Czy to błąd?
  2. Jeśli nie, czy istnieje inny warunek, którego mogę użyć lub czy muszę to zrobić, korzystając z limitów czasu?
  3. Czy jest jakiś problem ze sposobem wiązania zdarzenia obciążenia i to jest mój problem?
+1

myślę dokonywania Fiddle byłoby pomocne – Brewal

+1

Może być duplikatem http://stackoverflow.com/questions/8060429/when-is-a-stylesheet-added-to-document-stylesheets –

+0

@Brewal Mogę to wypróbować, ale zdarza się to mniej niż 10% czasu w moich prawdziwych przypadkach testowych, więc nie wiem, jak by to było pomocne. – Dan

Odpowiedz

6

Dynamicznie ładowanie arkuszy stylów CSS wciąż jest obszarem wypełnionym dziwactwami przeglądarki, niestety. W pakiecie Webkit elementy <style> i <link> będą ładować fire load and error events podczas ładowania arkuszy stylów. Jednak samo zdarzenie load oznacza tylko, że zasób arkusza stylów został załadowany, niekoniecznie dlatego, że został dodany do document.styleSheets.

Ładowarka require-css RequireJS zajmuje się tym problemem przez rozgałęzienia jego mechanizm ładowania w oparciu o userAgent wąchania (jest to prawie niemożliwe do funkcji wykrywania czy tag <link> zadziała jego load zdarzenia prawidłowo). Specjalnie dla Webkit, ośrodki detekcyjne do korzystania setTimeout znaleźć, gdy obiekt StyleSheet został dołączony do document.styleSheets

var webkitLoadCheck = function(link, callback) { 
    setTimeout(function() { 
    for (var i = 0; i < document.styleSheets.length; i++) { 
     var sheet = document.styleSheets[i]; 
     if (sheet.href == link.href) 
     return callback(); 
    } 
    webkitLoadCheck(link, callback); 
    }, 10); 
} 

Więc gdy zdarzenie load zadziała na Webkit, to jest niewiarygodne dla celów jest w stanie uzyskać dostęp do odpowiednia instancja StyleSheet. Obecnie jedynym silnikiem obsługującym zdarzenia stylów load jest poprawnie Firefox 18+.

Ujawnienie: Jestem współpracownikiem wymagają-css

Referencje:

0
  1. myślę zdarzenie obciążenie mogłoby być wywołane wcześniej niż arkusza stylów CSS. Problem może się zdarzyć 1 z 10 razy, ale nadal nie jest to najlepsza praktyka i najbardziej czysty sposób.

  2. setTimeout to z pewnością dobre podejście, ponieważ można zagwarantować, że twój skrypt ładuje najpierw css. Ponieważ jednak tylko znacznik arkusza stylów oznacza, że ​​tworzy się link do niego, najpierw trzeba pobrać css, więc niezależnie od tego, z jakiego podejścia skorzystasz, nigdy nie ma gwarancji, że twoje css zostanie załadowane przed zdarzeniem load pożary.

Aby mieć 100% pewność, że wszystko przebiega w odpowiedniej kolejności jest, aby dokonać synchronicznego FileRead pośrednictwem AJAX lub asynchroniczne wywołanie AJAX z funkcji pomocnika, który sprawdza czy CSS jest gotowy (ten sposób nie zamrażać Przeglądarka, ale nadal może sprawdzić, czy plik jest załadowany, czy nie). Innym sposobem jest po prostu powiązanie podręcznika arkuszy stylów: Najpierw ustawisz arkusze stylów, a następnie kod w nagłówku pliku HTML.

zrobił Patrzę więc w głąb wydarzeń obciążenia, ale może się okazać, że zdarzenie DOMContentLoaded jest wypalanie tylko CSS jest ładowany (il badań na ten jeden)

Spójrz na podobne pytanie: jQuery event that triggers after CSS is loaded?

+0

Nie podążam za tym - co może być bardziej synchroniczne niż tworzenie elementu 'style' i dołączanie go do DOM? Zapomniałem też wspomnieć - kod, o którym mówię, zazwyczaj nie działa przy ładowaniu strony, ale później. – Dan

+0

I po to, żeby być czystym (może nie było to wystarczająco jasne na moje pytanie) - nie ładuję zewnętrznego arkusza stylów tutaj. Tworzę wewnętrzny z tekstem reguł generowanych za pomocą kodu JS. – Dan

0

Nie twierdzę, że mam rozwiązanie, ani wyjaśnienie czasu ładowania Webkita, ale ostatnio miałem podobny problem, ale z JS: Musiałem być absolutnie pewny, że framework jQuery został załadowany, zanim dołączę inne biblioteki mój własny, który zależał od jQuery.

Zauważyłem, że zdarzeniez jQuery nie zostało zwolnione, gdy się tego spodziewałem.

Na co warto, oto jak to rozwiązać mój problem:

var addedHead = window.document.createElement('link'); 

addedHead.async = true; 
addedHead.onload = addedHead.onreadystatechange = function() { 

    if (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') 
    { 
     callback(); 
     addedHead.onload = addedHead.onreadystatechange = null; 
    } 
}; 
//And then append it to the head tag 

Można było spróbować!

+0

Ponownie, nie jest to element 'link', jest to element' style' z regułami wstawianymi. Nie ma zaangażowanych zasobów zewnętrznych. – Dan

0

Taka sama potrzeba, za pomocą wykrywania reguły stylu, aby sprawdzić, czy arkusz stylów został załadowany.

// Load CSS dynamically. There's no way to determine when stylesheet has been loaded 
// so we usingn hack - define `#my-css-loaded {position: absolute;}` rule in stylesheet 
// and the `callback` will be called when it's loaded. 
var loadCss = function(url, cssFileId, callback){ 
    // CSS in IE can be added only with `createStyleSheet`. 
    if(document.createStyleSheet) document.createStyleSheet(url) 
    else $('<link rel="stylesheet" type="text/css" href="' + url + '" />').appendTo('head') 

    // There's no API to notify when styles will be loaded, using hack to 
    // determine if it's loaded or not. 
    var $testEl = $('<div id="' + cssFileId + '" style="display: none;"></div>').appendTo('body')  
    var checkIfStyleHasBeenLoaded = function(){ 
    if($testEl.css('position') == 'absolute'){ 
     $testEl.remove() 
     callback() 
    }else setTimeout(checkIfStyleHasBeenLoaded, 10) 
    } 
    setTimeout(checkIfStyleHasBeenLoaded, 0) 
} 
Powiązane problemy