web-dev-qa-db-ja.com

リンク要素のオンロード

とにかく<link>要素のonloadイベントをリッスンしますか?

F.ex:

var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'styles.css';

link.onload = link.onreadystatechange = function(e) {
    console.log(e);
};

これは<script>要素では機能しますが、<link>では機能しません。別の方法はありますか?外部スタイルシートのスタイルがいつDOMに適用されたかを知る必要があるだけです。

更新:

非表示の<iframe>を挿入し、<link>をヘッドに追加して、iframeでwindow.onloadイベントをリッスンするのはよい考えでしょうか。 CSSが読み込まれるとトリガーされますが、トップウィンドウに読み込まれるとは限りません...

40
David Hellsing

これは一種のハックですが、CSSを編集できる場合は、この投稿のテクニックを使用してリッスンできる特別なスタイル(目に見える効果なし)を追加できます。 http://www.west -wind.com/weblog/posts/478985.aspx

CSSが影響するクラスまたはIDを持つページの要素が必要になります。スタイルが変更されたことをコードが検出すると、CSSが読み込まれます。

私が言ったように、ハック:)

7
Peter Jaric

現在、すべての最新ブラウザーはリンクタグのonloadイベントをサポートしています。そのため、img要素の作成やonerrorの設定などのハックをガードします。

if !('onload' in document.createElement('link')) {
  imgTag = document.createElement(img);
  imgTag.onerror = function() {};
  imgTag.src = ...;
} 

これにより、FF-8以前および古いSafariおよびChromeバージョンの回避策が提供されます。

マイナーアップデート:

Michaelが指摘したように、常にハックを適用したいブラウザの例外がいくつかあります。 Coffeescriptでは:

isSafari5: ->
  !!navigator.userAgent.match(' Safari/') &&
      !navigator.userAgent.match(' Chrom') &&
      !!navigator.userAgent.match(' Version/5.')

# Webkit: 535.23 and above supports onload on link tags.
isWebkitNoOnloadSupport: ->
  [supportedMajor, supportedMinor] = [535, 23]
  if (match = navigator.userAgent.match(/\ AppleWebKit\/(\d+)\.(\d+)/))
    match.shift()
    [major, minor] = [+match[0], +match[1]]
    major < supportedMajor || major == supportedMajor && minor < supportedMinor
18
mlangenberg

Chrome(他のブラウザではテストされていません)でそれを行った方法は、Imageオブジェクトを使用してCSSをロードし、そのonerrorイベントをキャッチすることです。ブラウザはこのリソースが画像であるかどうかを知らないため、とにかくそれをフェッチしようとしますが、実際の画像ではないため、onerrorハンドラがトリガーされます。

var css = new Image();
css.onerror = function() {
    // method body
}
// Set the url of the CSS. In link case, link.href
// This will make the browser try to fetch the resource.
css.src = url_of_the_css;

リソースがすでにフェッチされている場合、このフェッチ要求はキャッシュにヒットすることに注意してください。

3
bellpeace

例えば。 Androidブラウザは要素の "onload"/"onreadystatechange"イベントをサポートしていません: http://pieisgood.org/test/script-link-events/
しかし、それは返します:

"onload" in link === true

したがって、私の解決策は、userAgentからAndroid browserを検出して、スタイルシートの特別なcssルールを待つことです(たとえば、「body」マージンのリセット)。
Androidブラウザではなく、 "onload"イベントをサポートしている場合、それを使用します:

var userAgent = navigator.userAgent,
    iChromeBrowser = /CriOS|Chrome/.test(userAgent),
    isAndroidBrowser = /Mozilla\/5.0/.test(userAgent) && /Android/.test(userAgent) && /AppleWebKit/.test(userAgent) && !iChromeBrowser; 

addCssLink('PATH/NAME.css', function(){
    console.log('css is loaded');
});

function addCssLink(href, onload) {
    var css = document.createElement("link");
    css.setAttribute("rel", "stylesheet");
    css.setAttribute("type", "text/css");
    css.setAttribute("href", href);
    document.head.appendChild(css);
    if (onload) {
        if (isAndroidBrowser || !("onload" in css)) {
            waitForCss({
                success: onload
            });
        } else {
            css.onload = onload;
        }
    }
}

// We will check for css reset for "body" element- if success-> than css is loaded
function waitForCss(params) {
    var maxWaitTime = 1000,
        stepTime = 50,
        alreadyWaitedTime = 0;

    function NeXTSTEP() {
        var startTime = +new Date(),
            endTime;

        setTimeout(function () {
            endTime = +new Date();
            alreadyWaitedTime += (endTime - startTime);
            if (alreadyWaitedTime >= maxWaitTime) {
                params.fail && params.fail();
            } else {
                // check for style- if no- revoke timer
                if (window.getComputedStyle(document.body).marginTop === '0px') {
                    params.success();
                } else {
                    NeXTSTEP();
                }
            }
        }, stepTime);
    }

    NeXTSTEP();
}

デモ: http://codepen.io/malyw/pen/AuCtH

3

私のハックが気に入らなかったので:)私は他の方法を探してみたところ、 one by brothercake が見つかりました。

基本的に、CSSはキャッシュされるため、AJAXを使用してCSSを取得し、ブラウザにキャッシュさせ、リンクの負荷を瞬時に処理することをお勧めします。これはおそらく毎回機能するとは限りません(たとえば、一部のブラウザではキャッシュがオフになっている可能性があるため)、ほとんどの場合。

1
Peter Jaric

これを行う別の方法は、ロードされているスタイルシートの数を確認することです。例えば:

「css_filename」を使用すると、CSSファイルのURLまたはファイル名、CSSが読み込まれたときにコールバック関数を「コールバック」します。

var style_sheets_count=document.styleSheets.length;
var css = document.createElement('link');
css.setAttribute('rel', 'stylesheet');
css.setAttribute('type', 'text/css');
css.setAttribute('href', css_filename);
document.getElementsByTagName('head').item(0).appendChild(css);
include_javascript_wait_for_css(style_sheets_count, callback, new Date().getTime());

function include_javascript_wait_for_css(style_sheets_count, callback, starttime)
/* Wait some time for a style sheet to load.  If the time expires or we succeed
 *  in loading it, call a callback function.
 * Enter: style_sheet_count: the original number of style sheets in the
 *                           document.  If this changes, we think we finished
 *                           loading the style sheet.
 *        callback: a function to call when we finish loading.
 *        starttime: Epoch when we started.  Used for a timeout. 12/7/11-DWM */
{  
   var timeout = 10000; // 10 seconds
   if (document.styleSheets.length!=style_sheets_count || (new Date().getTime())-starttime>timeout)
      callback();
   else
      window.setTimeout(function(){include_javascript_wait_for_css(style_sheets_count, callback, starttime)}, 50);
}
1
garlon4

このトリックは xLazyLoader jQueryプラグイン から借用したものです。

var count = 0;

(function(){
  try {
    link.sheet.cssRules;
  } catch (e) {
    if(count++ < 100)
      cssTimeout = setTimeout(arguments.callee, 20);
    else
      console.log('load failed (FF)');
    return;
  };
  if(link.sheet.cssRules && link.sheet.cssRules.length == 0) // fail in chrome?
    console.log('load failed (Webkit)');
  else
    console.log('loaded');
})();

テスト済みで、FF(3.6.3)およびChrome(linux-6.0.408.1 dev)でローカルに動作しています。

デモ ここ (これは、FFのデモで行われているように、クロスサイトのCSSロードには機能しないことに注意してください)

0
sje397

これはクロスブラウザソリューションです

// Your css loader
var d = document,
    css = d.head.appendChild(d.createElement('link'))

css.rel = 'stylesheet';
css.type = 'text/css';
css.href = "https://unpkg.com/[email protected]/css/tachyons.css"

// Add this  
if (typeof s.onload != 'undefined') s.onload = myFun;
} else {
    var img = d.createElement("img");
    img.onerror = function() {
      myFun();
      d.body.removeChild(img);
    }
    d.body.appendChild(img);
    img.src = src;
}

function myFun() {
    /* ..... PUT YOUR CODE HERE ..... */
}

答えは このリンク に基づいています:

舞台裏で何が起きるかは、ブラウザがimg要素にCSSをロードしようとすることであり、スタイルシートは画像のタイプではないため、img要素はonerrorイベントをスローし、関数を実行します。ありがたいことに、ブラウザはCSSファイル全体をロードしてから画像ではないと判断し、onerrorイベントを発生させます。

最新のブラウザーでは css.onload を実行し、フォールバックとしてそのコードを追加して、only Opera and Internet Explorerは、onloadイベントとonreadystatechangeをそれぞれサポートしていました

注:私も ここ と回答しました。これも私の複製であり、正直さのために罰せられるに値します:P

0
user985399
// this work in IE 10, 11 and Safari/Chrome/Firefox/Edge
// if you want to use Promise in an non-es6 browser, add an ES6 poly-fill (or rewrite to use a callback)

let fetchStyle = function(url) {
  return new Promise((resolve, reject) => {
    let link = document.createElement('link');
    link.type = 'text/css';
    link.rel = 'stylesheet';
    link.onload = resolve;
    link.href = url;

    let headScript = document.querySelector('script');
    headScript.parentNode.insertBefore(link, headScript);
  });
};
0
sandstrom

知っているスタイルの特定の要素が必要か、CSSファイルを制御している場合は、この目的のためにダミー要素を挿入できます。このコードは、CSSファイルのコンテンツがDOMに適用されるときに、コールバックを正確に実行します。

// dummy element in the html
<div id="cssloaded"></div>

// dummy element in the css
#cssloaded { height:1px; }

// event handler function
function cssOnload(id, callback) {
  setTimeout(function listener(){
    var el = document.getElementById(id),
        comp = el.currentStyle || getComputedStyle(el, null);
    if ( comp.height === "1px" )
      callback();
    else
      setTimeout(listener, 50);
  }, 50)
}

// attach an onload handler
cssOnload("cssloaded", function(){ 
  alert("ok"); 
});

ドキュメントの下部でこのコードを使用する場合、要素を一度取得するために、タイマーの外にelおよびcomp変数を移動できます。ただし、ハンドラーをドキュメントのどこか(ヘッドなど)にアタッチする場合は、コードをそのままにしておきます。

注:FF 3 +、IE 5.5 +、Chromeでテスト済み

0
gblazex

XLazyLoaderプラグインは、cssRulesプロパティが他のドメインに属するスタイルシートに対して非表示になっているために失敗します(同じOriginポリシーに違反します)。だからあなたがしなければならないことはownerNodeとowningElementsを比較することです。

何をすべきかを徹底的に説明します: http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/

0
matsko