загрузка элемента ссылки - PullRequest
38 голосов
/ 20 июня 2010

Есть ли способ прослушать событие onload для элемента <link>?

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> в голову и прослушать событие window.onload в iframe? Он должен срабатывать при загрузке css, но может не гарантировать, что он загружен в верхнем окне ...

Ответы [ 11 ]

16 голосов
/ 28 ноября 2012

Сегодня все современные браузеры поддерживают событие onload для тегов ссылок.Поэтому я бы защитил хаки, такие как создание элемента img и установка onerror:

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

Это должно обеспечить обходной путь для FF-8 и более ранних и старых версий Safari & Chrome.

незначительное обновление:

Как указывал Майкл, есть некоторые исключения браузера, для которых мы всегда хотим применить взлом.В 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
7 голосов
/ 20 июня 2010

Это своего рода хак, но если вы можете редактировать CSS, вы можете добавить специальный стиль (без видимого эффекта), который вы можете прослушать, используя технику, описанную в этом посте: http://www.west -wind.com / weblog / posts / 478985.aspx

Вам потребуется элемент на странице, который имеет класс или идентификатор, на который будет влиять CSS.Когда ваш код обнаруживает, что его стиль изменился, CSS был загружен.

Хак, как я уже сказал:)

3 голосов
/ 21 января 2014

например. Браузер Android не поддерживает события «onload» / «onreadystatechange» для элемента: http://pieisgood.org/test/script-link-events/
Но это возвращает:

"onload" in link === true

Итак, мое решение состоит в том, чтобы обнаружить браузер Android от userAgent, а затем дождаться какого-то специального правила 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 голосов
/ 13 ноября 2012

Я сделал это в Chrome (не тестировался в других браузерах), чтобы загрузить CSS с помощью объекта Image и перехватить его событие 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;

Обратите внимание, что если ресурс уже получен, этот запрос на выборку попадет в кэш.

1 голос
/ 07 декабря 2011

Другой способ сделать это - проверить, сколько таблиц стилей загружено. Например:

С помощью "css_filename" URL или имя файла CSS и "обратный вызов" функции обратного вызова при загрузке 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 голос
/ 23 июня 2010

Так как вам не понравился мой хак :) Я искал другой способ и нашел один от brothercake .

По сути, предлагается получить CSS с помощью AJAXчтобы браузер кешировал его, а затем обрабатывал загрузку ссылки как мгновенную, поскольку CSS кэшируется.Это, вероятно, не будет работать каждый раз (так как, например, в некоторых браузерах может быть отключен кэш), но почти всегда.

0 голосов
/ 26 мая 2019

Это кросс-браузерное решение

// 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/tachyons@4.10.0/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 ..... */
}

Ответ основан на этой ссылке , которая говорит:

За кулисами происходит то, что браузер пытается загрузить CSS в элементе img и, поскольку таблица стилей не является типом image, элемент img генерирует событие onerror и выполняет наш функция. К счастью, браузеры загружают весь CSS-файл раньше определение его не изображения и запуск события onerror .

В современных браузерах вы можете сделать css.onload и добавить этот код в качестве запасного варианта для покрытия старых браузеров до 2011 года, когда только только Opera и Internet Explorer поддерживали событие onload и onreadystatechange соответственно .

Примечание: я также ответил здесь , и это мой дубликат, и он заслуживает наказания за мою честность: P

0 голосов
/ 02 декабря 2016
// 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 голосов
/ 24 марта 2011

Плагин xLazyLoader не работает, поскольку свойства cssRules скрыты для таблиц стилей, принадлежащих другим доменам (нарушает ту же политику происхождения).Итак, что вам нужно сделать, это сравнить ownerNode и owningElements.

Вот подробное объяснение того, что нужно сделать: http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/

0 голосов
/ 29 июня 2010

Этот прием заимствован из плагина 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)

Демо здесь (обратите внимание, что это не будет работать для межсайтовой загрузки CSS, как это делается в демо, под FF)

...